今天给大家分析一下如何爬取豆瓣网的话题帖子内容

进入到豆瓣官网的小组页面:https://www.douban.com/group/explore

发现这里有个搜索框,而我们要调用的就是这个豆瓣内部搜索框来爬取我们需要的话题内容

任意输入一个搜索词,点开f12控制台,点击搜索按钮,选择话题选项卡,然后在network下找到发送的请求地址信息

然后我们重点需要的信息是:

1:

Request URL:
https://www.douban.com/group/search?cat=1013&q=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6
2:headers和param参数

获取到以上信息后就可以开始写爬虫了

首先创建一个py文件,然后引入必要的包

import re,urllib
from bs4 import BeautifulSoup
import datetime, time

然后创建一个类:

class DouBanCrawler(object):
在这个类下写所有逻辑代码

1:初始化数据方法:
    def __init__(self):
        '''
        Constructor
        '''
        self.session = SessionCrawler(sleepRange=[3, 8])
        self.headers = {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.8',
            'Cache-Control': 'max-age=0',
            'Connection': 'keep-alive',
            'Host': 'www.douban.com',
            'Upgrade-Insecure-Requests': '1'
        }

这段代码我将headers参数全局话调用,内容是刚刚用f12查看到的

然后我用的是session工具来进行http请求,其中

self.session = SessionCrawler(sleepRange=[3, 8])
SessionCrawler是我封装的外部类
内容如下:
import requests
import time
import random
import traceback

class SessionCrawler(object):
    '''
    classdocs
    '''

    def __init__(self, session=None, sleepRange=[1,2], logger=None):
        '''
        Constructor
        '''
        if session is None:
            self.session = requests.session()
        else:
            self.session = session
      
        self.sleepRange = sleepRange
        self.lastCrawlTime = 0
    
    def get(self, url, textRspOnly=True, **kwargs):
        #self.randomSleep()
        result = self.session.get(url,**kwargs)
        self.lastCrawlTime = time.time()
        if textRspOnly:
            return result.text
        else:            
            return result
def randomSleep(self):
if time.time() - self.lastCrawlTime < self.sleepRange[0]: sleepTime =self.sleepRange[0] + (self.sleepRange[1]-self.sleepRange[0])*random.random() time.sleep(sleepTime)

调用外部库的时候这么写:

from com.naswork.sentiment.crawler.sessioncrawler import SessionCrawler
com.naswork.sentiment.crawler.sessioncrawler是外部文件库存放地址

做好初始化准备工作后我们就可以正式分析爬虫如何写了

    def searchArticle(self, keywordList, endTime):
        '''
        根据关键字数组,开始时间和结束时间范围搜索文章
        @param keywordList: 关键字数组
        @param endTime: 搜索时间范围结束
        '''
        startTime = endTime - datetime.timedelta(hours=1)
        page = 0 #是否翻页爬取
        articleList = list()
        hasnext = True  #是否有下一条符合记录的帖子需要爬取
        while hasnext:
            (articleListInPage, hasnext) = self.__searchByPage(keywordList, startTime, endTime, page)
            articleList.extend(articleListInPage)
            page += 50
        return articleList
searchArticle方法用来当作程序的入口
self.__searchByPage(keywordList, startTime, endTime, page)就是插入搜索关键词,
然后设置搜索话题的发布时间范围和搜索页数
具体代码如下:
    def __searchByPage(self,keywordList,startTime,endTime,page):
        # 在豆瓣内部搜索框搜索
        page = str(page) #url接收的是str格式
        search_url = "https://www.douban.com/group/search?start="+page+"&cat=1013&sort=time&q="
        #cat:按话题搜索  sort:按最新发布时间分类  q:搜索关键词
        query = urllib.quote(' '.join(keywordList).encode('utf-8'))
        search_url = search_url+str(query)

        url_page = self.session.get(search_url,headers=self.headers)
        soup = BeautifulSoup(url_page, "lxml")
        main_wrap = soup.find('div', attrs={'class': "article"})
        main_article_list = main_wrap.find('div',attrs={'class':"topics"})
        articleList = list()
        hasnext = True
        if main_article_list is not None:
            title_list = main_article_list.findAll('tr', {'class': 'pl'})
            for title in title_list:
                article_publishtime = title.find('td', attrs={'class': "td-time"}).attrs['title']
                urlTime = time.strptime(article_publishtime, "%Y-%m-%d %H:%M:%S")
                Y, M, D, H = urlTime[0:4]
                urlTime2 = datetime.datetime(Y, M, D, H)
                urlTime2 = time.mktime(urlTime2.timetuple())
                #转换成时间戳来比较 float类型
                startTime = endTime - datetime.timedelta(days=2)
                startTimeIntSecond = time.mktime(startTime.timetuple())
                endTimeIntSecond = time.mktime(endTime.timetuple())

                #如果符合时间范围就爬取
                if urlTime2 >= startTimeIntSecond and urlTime2 <= endTimeIntSecond:
                    article_url = title.find('a').attrs['href']
                    article_title = title.find('td', attrs={'class': "td-subject"}).text
                    article = Article(article_title,article_publishtime, article_url, None)
                    reply_count = title.find('td', attrs={'class': "td-reply"}).text.strip()
                    reply_count = re.sub(u'回应', '', reply_count)  # 回复数量去除中文保留数字
                    if article not in articleList:
                        articleList.append(article)

                else:
                    print len(articleList)
                    hasnext=False
                    break

            return (articleList, hasnext)

重点是这段代码:

 url_page = self.session.get(search_url,headers=self.headers)
 soup = BeautifulSoup(url_page, "lxml")
将豆瓣url放入get()中发送http请求,并且发送headers请求头,返回数据保存到url_page变量中,
再接着调用BeautifulSoup插件库方法,第二个参数是html解析器
调用后就可以将获取到的url_page(string)格式的网页源码以html格式编码
那么,就可以通过find() findAll()来进行html标签的获取查找了
比如说我要获取下面的内容:

那么可以这么写:

main_wrap = soup.find('div', attrs={'class': "article"})
那么,main_wrap变量保存的就是图中div标签(class=article)的包裹内容以及它本身
然后要找main_wrap里的内容也可以通过find查找:
main_children = main_wrap.find('div', attrs={'class': "srh-filter"})
总之一句话:获取父标签的对象,然后通过父标签对象一层层往下找子对象内容

然后
reply_count = re.sub(u'回应', '', reply_count)  # 回复数量去除中文保留数字
这里的re.sub()是可以去除我们不想要的内容,例如:123回应
将 回应 二字设为‘’ (空)就可以只保留123 数字了

Article()类是额外写的用来保存文章信息。也就是写一个用来保存数据的返回类。
 

 

 

posted on 2018-02-05 23:11  YKing_匆  阅读(2108)  评论(0)    收藏  举报