欢迎光临散文网 会员登陆 & 注册

[Scrapy爬虫实战]Discuz论坛版块内全部帖子获取

2020-07-26 23:05 作者:热心的裴同学  | 我要投稿

先插入封面(老惯例了)

高清无码PDF见:

链接:https://pan.baidu.com/s/1qD0IBElUFTFv0F34QV6vIA 

提取码:0e6n

 

项目源码见:

链接:https://pan.baidu.com/s/1OF5EUFTCuv4n_6GJ_MCv-g 

提取码:p666


如果你想了解更过关于Python爬虫的相关知识建议(不要来问我,我也是新手)

欢迎加入群聊【来自裴同学的问候(1006320973)】:点击此链接或搜索QQ群号码https://jq.qq.com/?_wv=1027&k=RZs0P4LO



正文开始:


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

 

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

 

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

 

要爬取的页面地址:

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

 

 

创建工程

 ps: 和其他字不一样颜色的是我执行的命令

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


打开工程

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



修改配置

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")


2. 遍历每一项取出其中的信息 其中每一个元素标签上都有一个唯一的id 作为帖子的id 但第一项2没有

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)




3. 爬取帖子具体内容

# 取出已经准备好的信息
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")


遍历帖子列表

# 准备存放所有评论的列表
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)



结果

存在于data.json中 


[Scrapy爬虫实战]Discuz论坛版块内全部帖子获取的评论 (共 条)

分享到微博请遵守国家法律