Scrapy糗事百科爬虫实战代码分析

Scrapy糗事百科爬虫实战代码分析

视频教学网址:【python爬虫_从入门到精通(高级篇)】scrapy框架、反爬、分布式爬虫

一、Scrapy糗事百科之爬取单页数据并保存

具体的创建方法可以参照上一篇文章Python最火爬虫框架Scrapy入门与实践
创建的目录结构如下:
Scrapy糗事百科爬虫实战代码分析_第1张图片

在qsbk_spider.py文件中,response是一个’scrapy.http.response.html.HtmlResponse’对象.可以执行’xpath’和’css’语法来提取数据。

# -*- coding: utf-8 -*-
import scrapy
from qsbk.items import QsbkItem


class QsbkSpiderSpider(scrapy.Spider):
    name = 'qsbk_spider'
    allowed_domains = ['qiushibaike.com']
    start_urls = ['https://www.qiushibaike.com/text/page/1/']

    def parse(self, response):
        # SelectorList
        duanzidivs =  response.xpath("//div[@class='col1 old-style-col1']/div")
        # 遍历div
        for duanzidiv in duanzidivs:
            # Selector-->Unicode
            author =  duanzidiv.xpath(".//a/h2/text()").get().strip()
            content = duanzidiv.xpath(".//div[@class='content']//text()").getall()
            content = " ".join(content).strip()
            # 提取出来的数据,是一个’Selector’或者是一个’SelectorList’对象。如果想要获取其中的字符串,那么应该执行’getall’或是’get’方法
            # getall方法:获取Selector中的所有文本,返回的是一个列表。get方法:是获取selector中的第一个文本,返回的是str类型.

            item = QsbkItem(author=author,content=content)
			
			#如果数据解析回来,要传给pipline处理.那么可以使用’yield’来返.或是可以收集到所有的item,最后统一使用return返回.
            yield item

在itmes.py文件中,用来存放爬虫爬取下来的数据类型,里面的数据与qsbk_spider.py 定义的 一 一对应。

import scrapy
# item:建议在item.py中定义好模型.以后就不要使用字典了.在最后将数据变成字典就行了
class QsbkItem(scrapy.Item):
    author = scrapy.Field()
    content = scrapy.Field()

在piplines文件中,用来将items的模型存储到本地磁盘中;
pipeline:这个是专门用来保存数据地.其中三个方法是会经常用到的
①open_spider(self,spider):当爬虫被打开的时候就会被调用
②process_item(self,item,spider):当爬虫有item传过来的时候就会被调用
③close_spider(self,spider):当爬虫关闭的时候会被调用

要激活piplilne , 应该在settings.py中设置ITEM_PIPELINES将其激活.

import json

class QsbkPipeline(object):
    def __init__(self):
        self.fp = open("duanzi.json",'w',encoding="utf-8") # 创建一个导入的对象

    def open_spider(self,spider):
        print("爬虫开始了。。")

    def process_item(self, item, spider):
        # 将item转换为字典,再将字典转换为json ;item是爬虫返回的数据
        item_json = json.dumps(dict(item),ensure_ascii=False) 
        # json字符串写入文件当中
        self.fp.write(item_json+'\n')
        return item

    def close_spider(self,spider):
        self.fp.close()
        print("爬虫结束了。。")

settings.py: 本爬虫的一些配置信息(比如请求头、多久发送一次、ip代理池等等),需要将下面的注释去掉。

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Override the default request headers:
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; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36 SLBrowser/6.0.1.4221'
}

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'qsbk.pipelines.QsbkPipeline': 300, #值越小优先级越高;当设置多个爬虫时,可以参考此值。
}

由于每一次在命令端调试比较麻烦,可以在qsbk文件的根目录创建start.py文件进行调试。

from scrapy import cmdline
# 调式
cmdline.execute("scrapy crawl qsbk_spider".split())
# cmdline.execute(['scrapy', 'crawl', 'qsbk_spider'])

二、Scrapy糗事百科之优化数据存储方式

在pipelines.py文件里保存json数据的时候,可以使用这两个类,让操作更加简单。
①JsonItemExporter:这个是每次把数据添加到内存中.最后统一写入到磁盘中.
好处就是存储的数据是一个满足json规则的数据.
坏处就是如果数据量比较大,那么比较消耗内存

from scrapy.exporters import JsonItemExporter
class QsbkPipeline(object):
    def __init__(self):
        # 二进制打开文件,因为JsonItemExporter是以二进制写入写入的
        self.fp = open("duanzi.json",'wb')
        self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding="utf-8")
        # 开始导入
        self.exporter.start_exporting()

    def open_spider(self,spider):
        print("爬虫开始了。。")

    def process_item(self, item, spider):
        # 先把传进来的字典塞到一个列表当中
        self.exporter.export_item(item)
        return item

    def close_spider(self,spider):
        # 完成导入
        self.exporter.finish_exporting() # 再统一写道json文件中

json文件存储一行数据:

[{"author": "山鹰寂寞飞", "content": "那年那人那山第三十二章:钱去楼空 红军很惊讶的问:“怎么了?是怪我昨天没来接你吗?”秀芬面无表情,眼角泪珠往下飞速滚淌着。   红军凑上前,蹲下握住秀芬的手道:“好了,我知道错了,下次不管再晚,哪怕骑车也过来接你,好不好?”    秀芬触电一般用力拽回自己的手,歇斯底里大叫一句:“你走啊!我不想再看到你!”    红军吓了一跳,见秀芬如此激动,好像真想分手,有点莫名其妙,他盯了秀芬一会,眼见她侧脸昂头,只流泪不再说话,态度相当坚决,思忖一会问:“为什么?总得给我个理由吧?是…因为我没\n…\n \n\n 查看全文"}]......

②JsonLinesItemExporter:这个是每次调用export_item的时候就吧这个item存储到硬盘当中.
坏处是每一个字典是一行,整个文件不是一个满足json格式的文件.
好处就是每次处理数据的时候就直接存储到硬盘当中,这样不会消耗内存,数据也比较安全.

from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline(object):
    def __init__(self):
        # 二进制打开文件,因为JsonItemExporter是以二进制写入写入的
        self.fp = open("duanzi.json",'wb')
        self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding="utf-8")

    def open_spider(self,spider):
        print("爬虫开始了。。")

    def process_item(self, item, spider):
        # 先把传进来的字典塞到一个列表当中
        self.exporter.export_item(item)
        return item

    def close_spider(self,spider):
        self.fp.close()
        print("爬虫结束了。。")

输出结果:一个字典一行的存储:

{"author": "山鹰寂寞飞", "content": "那年那人那山第三十三章:炼狱化疗 传说中的化疗很快来到,秀芬和红军以为是什么工程浩大惊天动地的疗法,没成想也就是几瓶药水输进身体。    然而他们都小瞧了这几瓶药水的威力,刚挂上没一会,楚建国开始恶心,说胃里火烧火燎像有硫酸在腐蚀,不停的干呕着,后来实在憋持不住,让秀芬赶紧拿来垃圾桶,他侧倒在床沿翻江倒海的抽搐着身子,一阵又一阵吐得眼泪直流。    早上吃的东西全都吐了个干净,顺着嘴角又一佝偻一佝偻呕出黄疸水,偌大的一个壮汉,竟然像个孩子似的,吐的哭出了声音。    秀芬很害怕,去找值班医生,\n…\n \n\n 查看全文"}
{"author": "五少爷的刀", "content": "中午在食堂吃午饭的时候,刚好和厂长坐在一起,为了活跃气氛,我就讲了两个笑话。 厂长果然被我逗笑了,噗~的一声,把他刚扒进嘴里的饭全喷在我碗里了。。。 大家都放下了碗筷,扭头看我。 就在我在考虑是不是不要了的时间,厂长带的二哈跳起来把我的饭吃了!…… 谢天谢地啊,谢天谢地!!!……我站起来,朝着二哈,一躬到地。。。"}
{"author": "精灵不次饭~", "content": "穿了条新裙子,照镜子觉得特别好看。我脑子一抽就发视频给在玩游戏的老公,问他好看不,还跟他卖个萌做了个鬼脸。 现在我很想把他踹出家门!!这货不停的追问我“你直接喊我来看不就行了吗?”“我在家呢,你发视频干啥?”“你是不是发错人了?” “你本来打算发给谁的?”"}
.......

三、Scrapy糗事百科之抓取多个页面

修改qsbk_spider.py,分析"下一页"按钮的标签结构
Scrapy糗事百科爬虫实战代码分析_第2张图片

# -*- coding: utf-8 -*-
import scrapy
from qsbk.items import QsbkItem

class QsbkSpiderSpider(scrapy.Spider):
    name = 'qsbk_spider'
    allowed_domains = ['qiushibaike.com']
    start_urls = ['https://www.qiushibaike.com/text/page/1/']
    base_domain = "https://www.qiushibaike.com"

    def parse(self, response):
        # SelectorList
        duanzidivs =  response.xpath("//div[@class='col1 old-style-col1']/div")
        # 遍历div
        for duanzidiv in duanzidivs:
            # Selector-->
            author =  duanzidiv.xpath(".//a/h2/text()").get().strip()
            content = duanzidiv.xpath(".//div[@class='content']//text()").getall()
            content = " ".join(content).strip()

            item = QsbkItem(author=author,content=content)

            yield item

        next_url = response.xpath("//ul[@class='pagination']/li[last()]/a/@href").get()
        if not next_url:
            return
        else:
            yield scrapy.Request(self.base_domain+next_url,callback=self.parse)
            # 返回当前的请求

在setting文件中,找到下面的注释掉,可以调成一秒获取一次数据

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 1

你可能感兴趣的:(Python,python,json,xpath,scrapy)