美文网首页
【scrapy爬虫实战】Discuz 论坛版块全部帖子信息爬取

【scrapy爬虫实战】Discuz 论坛版块全部帖子信息爬取

作者: 热心的裴同学 | 来源:发表于2020-07-26 22:08 被阅读0次

    Discuz 论坛模块全部帖子和评论爬取

    Discuz 是一款由PHP编写的开源论坛

    Discuz 官方论坛: https://www.discuz.net/forum.php

    image.png

    要爬取的页面地址:

    Discuz BUG与问题交流板块;https://www.discuz.net/forum-70-1.html

    创建工程

    scrapy startproject discuz

    
    C:\Users\PeiJingbo\Desktop\discuz>scrapy startproject discuz
    New Scrapy project 'discuz', using template directory 'c:\program files\python37\lib\site-packages\scrapy\templates\project', created in:
        C:\Users\PeiJingbo\Desktop\discuz\discuz
    
    You can start your first spider with:
        cd discuz
        scrapy genspider example example.com
    
    C:\Users\PeiJingbo\Desktop\discuz>
    
    
    cd discuz
    

    创建爬虫

    scrapy genspider discuz_spider discuz,net
    
    C:\Users\PeiJingbo\Desktop\discuz\discuz>scrapy genspider discuz_spider discuz,net
    Created spider 'discuz_spider' using template 'basic' in module:
      discuz.spiders.discuz_spider
    
    

    打开工程

    image.png

    应该打开创建项目命令生成的那个目录 如果选择再下层目录 就不能导模块了

    修改配置

    settings,py

    ROBOTSTXT_OBEY = False  # 不遵循ROBOTS协议
    
    DEFAULT_REQUEST_HEADERS = {     # 设置默认请求头
      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
      'Accept-Language': 'en',
      'user-agent': ' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36'
    }
    
    ITEM_PIPELINES = {  # 启用 PIPELINES
       'discuz.pipelines.DiscuzPipeline': 300,
    }
    
    DOWNLOAD_DELAY = 0.1    # 下载延时
    
    

    存储

    item.py

    # 每一条帖子的信息
    class Post(scrapy.Item):
        pid = scrapy.Field()
        author = scrapy.Field()
        title = scrapy.Field()
        time = scrapy.Field()
        url = scrapy.Field()
        post_list = scrapy.Field()
    
    # 每一条帖子评论的信息
    class PostItem(scrapy.Item):
        iid = scrapy.Field()
        author = scrapy.Field()
        time = scrapy.Field()
        content = scrapy.Field()
    
    

    pipelines.py

    class DiscuzPipeline:
        # 计数变量
        number = 0
        # 爬虫初始化
        def __init__(self):
            # 打开文件
            self.fp = open("data.json", 'wb')
            # 存储json的格式
            self.save_json = JsonItemExporter(self.fp, encoding="utf-8", ensure_ascii=False, indent=4)
            # 开始存储
            self.save_json.start_exporting()
    
        def close_spider(self, spider):
            # 关闭爬虫时 写入所有数据
            self.save_json.finish_exporting()
            # 关闭文件
            self.fp.close()
            print("共爬取 %d 项数据" % self.number)
    
        def process_item(self, item, spider):
            # 爬取到每一项
            print(self.number)
            self.number += 1
            # 转为json写入item
            self.save_json.export_item(item)
    
            return item
    
    

    开始爬取 (单页面)

    spider/discuz_spider.py

    1. 首先要获取当前页面的所有帖子的url 通知爬取帖子的函数去处理

      # 此页面中所有帖子的列表
              tbody = response.xpath("//table[@id='threadlisttableid']/tbody")
      
    image.png
    1. 遍历每一项取出其中的信息 其中每一个元素标签上都有一个唯一的id 作为帖子的id 但第一项没有

      for tb in tbody:
          # 取出这个元素的id 字符串
          p_id_str = tb.xpath("./@id").get()
          # 通过正则表达式取出数字id 这就是这个帖子的id
          p_id_match = re.match(r"normalthread_(\d+)", p_id_str)
          # 开头有一项是不带id的空项 为了跳过做判断
          if not p_id_match:
           continue
      
      # 取出数字id
      p_id = p_id_match.group(1)
      
      # 获取作者
      author = tb.xpath("./tr/td[2]//a/text()").get()
      
      
      # 获取页面url
      url = response.urljoin(tb.xpath("./tr/th/a[@class='s xst']/@href").get())
      

      其中时间有两种状态 第一种是 写着几天前 或者是几小时前这种 在文字标签有title属性就是具体日期

      第二张就是直接是直接为时间 判断如果第一种取出为空则取第二种

      # 获取时间 有两种状态
      time = tb.xpath(".//tr/td[2]/em/span/span/@title").get()
      if not time:
       time = tb.xpath(".//tr/td[2]/em/span/text()").get()
      
      

      通知下一个函数去爬取具体内容 并将帖子的基本信息传递过去

      # 通知下面函数进行爬取
      yield scrapy.Request(url, meta={"id": p_id, "url": url, "author": author, "time": time},callback=self.getPost)
      
    2. 爬取帖子具体内容

      # 取出已经准备好的信息
      p_id = response.meta["id"]
      p_author = response.meta["author"]
      p_time = response.meta["time"]
      p_url = response.meta["url"]
      # 获取帖子标题
      p_title = response.xpath("//*[@id='thread_subject']/text()").get()
      

      其中所有的内容都是以列表的形式展现的 结构一致

      # 获取评论列表
      content_list = response.xpath(".//*[@id='postlist']/div")
      
    image.png

    遍历帖子列表

    # 准备存放所有评论的列表
    p_content_list = []
    
    for c in content_list:
        # 评论编号
        cid_match = re.match(r"post_(\d+)", c.xpath("./@id").get())
        if not cid_match:
         continue
        # 取出数字编号
        cid = cid_match.group(1)
    
     # 评论作者
     author = c.xpath(".//div[@id='favatar" + cid + "']//a[@class='xw1']/text()").get()
    

    时间信息同样有两种状态 第二种带有 "发表于 " 字样

    # 评论时间 同样有两种状态
    c_time = c.xpath(".//*[@id='authorposton" + cid + "']/span/@title").get()
    if not c_time:
        c_time = str(c.xpath(".//*[@id='authorposton" + cid + "']/text()").get()).strip().replace("发表于 ", '')
    
    # 评论内容
    content = c.xpath("string(.//div[@class='t_fsz'])").get()
    

    存储

    # 构造一个评论元素
    post_item = PostItem(iid=cid, author=author, time=c_time, content=content)
    # 添加到列表
    p_content_list.append(post_item)
    

    列表遍历完成

    # 传递到pipelines
    new_post = Post(pid=p_id, author=p_author, title=p_title, time=p_time, post_list=p_content_list, url=p_url)
    # 传递给pipelines
    yield new_post
    

    多页面爬取

    获取下一页的url 定义一个类的变量来记录页数

    # 每爬取一页加一
            self.page += 1
    
    # 获取下一个页面的url
    
    next_url = response.xpath(".//div[@id='pgt']/span[@id='fd_page_top']/div[@class='pg']/a[@class='nxt']/@href").get()
    # 如果没有下一个按钮则退出程序
    # 这个列表有两千多项,,,加了个200的结束条件
    if not next_url or self.page >= 500:
        return
    # 将下一页url与主机名和协议进行组合
    next_url = response.urljoin(next_url)
    # 通知自己去请求下一页
    yield scrapy.Request(url=next_url, callback=self.parse)
    
    image.png

    查看结果

    结果存在data.json中


    image.png

    相关文章

      网友评论

          本文标题:【scrapy爬虫实战】Discuz 论坛版块全部帖子信息爬取

          本文链接:https://www.haomeiwen.com/subject/esoglktx.html