bs4爬虫实战一:获取百度贴吧内容

发布时间:2018-06-23 15:42:54编辑:Run阅读(5072)

    目标分析:

    进入百度贴吧,访问: https://tieba.baidu.com/index.html

    搜索权利的游戏

    blob.png



    定义需要爬取的数据,爬取每个帖子上面的内容

    blob.png


    定位到每个帖子,即取 li标签 里面 'class'=' j_thread_list clearfix' 的所有帖子,这个li里面就包含需要的所有内容

    title   # 帖子标题

    post_author   # 帖子创建作者

    post_time   # 帖子创建时间

    re_num   # 总回复数

    content   # 最后回复内容

    last_author   # 最后回复者

    last_time   # 最后回复时间


    blob.png



    获取下一页的url请求

    请求地址: http://tieba.baidu.com/f?kw=%E6%9D%83%E5%88%A9%E7%9A%84%E6%B8%B8%E6%88%8F&ie=utf-8&pn=50

    blob.png


    图中,可以很明确的看出,每按一次"下一页",pn将增加50,只需求拼接url地址的时候更改pn的值,就可以得到下一页的请求地址


    项目实施:

    新建一个get_tieba_info.py文件,代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    import urllib.request
    from bs4 import BeautifulSoup
    from mylog import MyLog as mylog  # 导入Mylog类,起别名为mylog
    
    
    class Item(object):  # 定义一个Item类,爬取的字段类
        title = None  # 帖子标题
        post_author = None  # 帖子创建作者
        post_time = None  # 帖子创建时间
        re_num = None  # 总回复数
        content = None  # 最后回复内容
        last_author = None  # 最后回复者
        last_time = None  # 最后回复时间
    
    
    class GetTieBaInfo(object):
        def __init__(self, url):
            self.url = url  # 接收一个初始的url地址
            self.log = mylog()  # 实例化mylog日志类
            self.pagesum = 5  # 控制爬取的页面数(一页50条数据)
            self.urls = self.geturls(self.pagesum)  # 获取所有下一页的urls列表
            self.items = self.spider(self.urls)  # 处理每一页的数据
            self.pipelines(self.items)  # 数据下载
    
        '''处理url地址,return urls地址列表'''
        def geturls(self, pagesum):
            urls = []
            pns = [str(i*50) for i in range(pagesum)]  # 得到列表['0','50','100'....]即下一页的数字
            # ['0', '50', '100', '150', '200']
    
            ul = self.url.split('=')  # 以=号为分隔符,切割url地址,得到下面结果
            # ['http://tieba.baidu.com/f?kw', '权利的游戏&ie', 'utf-8&pn', '0']
    
            for pn in pns:
                ul[-1] = pn  # ur[-1] = 0 ,50,100,150
                url = '='.join(ul)  # 以=为拼接符,拼接下一页的url地址
                urls.append(url)  # 添加到上面的urls列表中
            self.log.info(u'获取URLS成功')  # 写入日志文件
            return urls  # 返回urls地址列表
    
        def spider(self, urls):
            items = []
            for url in urls:
                # 调用self.getresponsecontent函数,返回整个html的内容
                HtmlContent = self.getresponsecontent(url) 
                # 使用bs4解析器进行过滤
                soup = BeautifulSoup(HtmlContent, 'lxml')
             
                # 找到所有符合规则的li标签,返回一个list
                tagsli = soup.find_all('li', attrs={'class':' j_thread_list clearfix'})
    
                for tag in tagsli:
                    item = Item()  # 实例化Item类
                    # 获取帖子标题
                    item.title = tag.find('a', attrs={'class':'j_th_tit '}).get_text().strip()
                    # 获取帖子创建作者
                    item.post_author = tag.find('span', attrs={'class':'frs-author-name-wrap'}).get_text().
                    strip()
                    # 获取帖子创建时间
                    item.post_time = tag.find('span', attrs={'class':'pull-right is_show_create_time'}).
                    get_text().strip()
                    # 总回复数
                    item.re_num = tag.find('span', attrs={'class':'threadlist_rep_num center_text'}).
                    get_text().strip()
                    # 最后回复内容
                    item.content = tag.find('div', attrs={'class':'threadlist_abs threadlist_abs_onlyline '}).
                    get_text().strip()
                    # 最后回复者
                    item.last_author = tag.find('span', attrs={'class': 'tb_icon_author_rely j_replyer'}).
                    get_text().strip()
                    # 最后回复时间
                    item.last_time = tag.find('span', attrs={'class':'threadlist_reply_date pull_right 
                    j_reply_data'}).get_text().strip()
                    items.append(item)  # 把所有的item,添加到items列表
                    self.log.info(u'获取标题为<<{}>>的项成功...'.format(item.title))
            return items
    
        def pipelines(self, items):  # 接收一个items列表
            filename = u'百度贴吧_权利游戏.txt'
            with open(filename, 'w', encoding='utf-8') as fp:
                for item in items:
                    fp.write('帖子标题:{}\n帖子创建作者:{}\n帖子创建时间:{}\n帖子总回复数:\
                    {}\n帖子最后回复内容:{}\n帖子最后回复者:{}\n帖子最后回复时间:{}\n\n\n\n'\
                             .format(item.title,item.post_author,item.post_time,item.re_num,item.content,item.last_author,item.last_time))
                    self.log.info(u'标题为<<{}>>的项输入到 {} 成功'.format(item.title, filename))
    
        def getresponsecontent(self, url):
            try:
                page = urllib.request.urlopen(url)
                html = page.read().decode('utf-8')
            except:
                self.log.error(u'python 返回 URL:{} 数据失败'.format(url))
            else:
                self.log.info(u'python 返回 URL:{} 数据成功'.format(url))
                return html
             
    
    
    if __name__ == '__main__':
        url = 'http://tieba.baidu.com/f?kw=%E6%9D%83%E5%88%A9%E7%9A%84%E6%B8%B8%E6%88%8F&ie=utf-8&pn=0'
        st = GetTieBaInfo(url)


    新建一个mylog.py日志文件,代码如下:

    import logging
    import getpass
    import sys
    
    
    # 定义MyLog类
    class MyLog(object):
        def __init__(self):
            self.user = getpass.getuser()  # 获取用户
            self.logger = logging.getLogger(self.user)
            self.logger.setLevel(logging.DEBUG)
    
            # 日志文件名
            self.logfile = sys.argv[0][0:-3] + '.log'  # 动态获取调用文件的名字
            self.formatter = logging.Formatter('%(asctime)-12s %(levelname)-8s %(message)-12s\r\n')
    
            # 日志显示到屏幕上并输出到日志文件内
            self.logHand = logging.FileHandler(self.logfile, encoding='utf-8')
            self.logHand.setFormatter(self.formatter)
            self.logHand.setLevel(logging.DEBUG)
    
            self.logHandSt = logging.StreamHandler()
            self.logHandSt.setFormatter(self.formatter)
            self.logHandSt.setLevel(logging.DEBUG)
    
            self.logger.addHandler(self.logHand)
            self.logger.addHandler(self.logHandSt)
    
        # 日志的5个级别对应以下的5个函数
        def debug(self, msg):
            self.logger.debug(msg)
    
        def info(self, msg):
            self.logger.info(msg)
    
        def warn(self, msg):
            self.logger.warn(msg)
    
        def error(self, msg):
            self.logger.error(msg)
    
        def critical(self, msg):
            self.logger.critical(msg)
    
    
    if __name__ == '__main__':
        mylog = MyLog()
        mylog.debug(u"I'm debug 中文测试")
        mylog.info(u"I'm info 中文测试")
        mylog.warn(u"I'm warn 中文测试")
        mylog.error(u"I'm error 中文测试")
        mylog.critical(u"I'm critical 中文测试")


    运行get_tieba_info.py:

    pycharm截图

    blob.png


    get_tieba_info.log日志文件生成截图:

    blob.png


    百度贴吧_权利游戏.txt截图:

    blob.png


    代码分析:

    mylog.py模块,主要是为程序提供log功能

    log功能很重要,在大量爬取的时候,没有log帮助定位,很难找到错误点


    主程序:

    get_tieba_info.py也很简单

    Item类定义需要获取的数据

    GetTieBaInfo类逻辑处理

    geturls方法 获取所有需要爬取的url地址

    spider方法 提取每个url地址的详细内容

    pipelines方法 处理数据,爬取到的数据储存方式,这里使用的是文本txt,这可以存入mysql,redis,mongo等等

    getresponsecontent方法 负责发送请求,拿到影响文件(html)



关键字