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

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353