scrapy学习笔记0828-上

1.使用item pipeline处理数据

如果我们在爬取到数据之后想对数据进行一些处理再进行存储的话,我们可以使用pipline,pipeline在pipeline.py中进行定义,例如:

class PriceConverterPipeline(object):
    exchange_rate = 8.5309
    def process_item(self, item, spider):
        price = float(item['price'][1:]) * self.exchange_rate
        item['price'] = '¥%.2f' % price
        return item

与其他地方不同的是,pipline并不需要继承特定的基类,只需要实现某些特定的方法,例如process_item(必须实现)、open_spider:Spider打开时(处理数据前)回调该方法、close_spider:Spider关闭时(处理数据后)回调该方法,from_crawler:创建Item Pipeline对象时回调该类方法。

Item:爬取到的一项数据(Item或字典)

Spider:爬取此项数据的Spider对象。

第二步:启用itempipeline,光是我们自己写了代码其实还不够,需要我们在scrapy设置中启动它。我们需要在settings.py中配置,如下:

ITEM_PIPELINES = {
 'example.pipelines.PriceConverterPipeline': 300,
 }

我们把想要启用的Item Pipeline 添加到这个字典中,其中每一项的键是每一个Item Pipeline类的导入 路径,值是一个0~1000的数字,同时启用多个Item Pipeline时, Scrapy根据这些数值决定各Item Pipeline处理数据的先后次序,数值小的在前。最后形成一条数据处理的流水线。

2.更加优雅的提取链接

Scrapy提供了一个专门用于提取链接的类LinkExtractor,在提取 大量链接或提取规则比较复杂时,使用LinkExtractor更加方便。

 from scrapy.linkextractors import LinkExtractor
 le = LinkExtractor(restrict_css='ul.pager li.next')
 links = le.extract_links(response)

创建一个LinkExtractor对象,使用一个或多个构造器参数描 述提取规则,这里传递给restrict_css参数一个CSS选择器表 达式。它描述出下一页链接所在的区域(在li.next下),调用LinkExtractor对象的extract_links方法传入一个 Response对象,该方法依据创建对象时所描述的提取规则,在 Response对象所包含的页面中提取链接,最终返回一个列表, 其中的每一个元素都是一个Link对象,即提取到的一个链接。

LinkExtractor所包含的各个参数,

allow:接收一个正则表达式或一个正则表达式列表,提取绝对url与 正则表达式匹配的链接,如果该参数为空(默认),就提取全 部链接。

deny:接收一个正则表达式或一个正则表达式列表,与allow相反, 排除绝对url与正则表达式匹配的链接。

allow_domains:接收一个域名或一个域名列表,提取到指定域的链接。

deny_domains:接收一个域名或一个域名列表,与allow_domains相反,排除 到指定域的链接。

restrict_xpaths:接收一个XPath表达式或一个XPath表达式列表,提取XPath表 达式选中区域下的链接。

restrict_css:接收一个CSS选择器或一个CSS选择器列表,提取CSS选择器选 中区域下的链接。

tags:接收一个标签(字符串)或一个标签列表,提取指定标签内的 链接,默认为['a', 'area']。

attrs:接收一个属性(字符串)或一个属性列表,提取指定属性内的 链接,默认为['href']。

process_value:接收一个形如func(value)的回调函数。如果传递了该参数, LinkExtractor将调用该回调函数对提取的每一个链接(如a的 href)进行处理,回调函数正常情况下应返回一个字符串(处 理结果),想要抛弃所处理的链接时,返回None。

3.自定义导出数据的格式

在Scrapy中,负责导出数据的组件被称为Exporter(导出器), Scrapy内部实现了多个Exporter,每个Exporter实现一种数据格式的导出。

目前支持的数据格式包括:JSON,CSV,XML,Pickle,Marshal

没有excel,也没有txt格式,这些需要我们自己去定义

 scrapy crawl books -o books.csv

-o books.csv指定了导出文件的路径

scrapy crawl books -t csv -o books1.data

-t参数中的数据格式字符串(如 csv、json、xml)为键,在配置字典FEED_EXPORTERS中搜索 Exporter,如果我们添加了自己定义的格式,可以在settings中定义,

FEED_EXPORTERS = {'excel': 'my_project.my_exporters.ExcelItemExporter'}

FEED_URI 导出文件路径, FEED_FORMAT 导出数据格式, FEED_EXPORT_ENCODING 导出文件编码(默认情况下json文件使用数字编码,其他使用 utf-8编码),FEED_EXPORT_FIELDS 导出数据包含的字段(默认情况下导出所有字段) 并指定次序。

每一个Exporter都是BaseItemExporter的一个子类, BaseItemExporter定义了一些抽象接口待子类实现export_item(self, item) 负责导出爬取到的每一项数据,参数item为一项爬取到的数 据,每个子类必须实现该方法。 start_exporting(self) 在导出开始时被调用,可在该方法中执行某些初始化工作。finish_exporting(self) 在导出完成时被调用,可在该方法中执行某些清理工作。

我们想要自定义导出数据的格式,只需要完成export_item即可,如下:

from scrapy.exporters import BaseItemExporter
 import xlwt
 class ExcelItemExporter(BaseItemExporter):
 def __init__(self, file, **kwargs):
 self._configure(kwargs)
 self.file = file
 self.wbook = xlwt.Workbook()
 self.wsheet = self.wbook.add_sheet('scrapy')
 self.row = 0
 def finish_exporting(self):
 self.wbook.save(self.file)
 def export_item(self, item):
 fields = self._get_serialized_fields(item)
 for col, v in enumerate(x for _, x in fields):
 self.wsheet.write(self.row, col, v)
 self.row += 1

这段代码需要我们自己创建一个py文件(与setting.py同级)并写在里面。

之后我们需要在setting.py中开启对它的调用,

FEED_EXPORTERS = {'excel': 'example.my_exporters.ExcelItemExporter'}

之后,成功运行!

 scrapy crawl books -t excel -o books.xls

4.下载图片与文件

Scrapy框架内部提供了两个Item Pipeline,专门用于下载文件和 图片:FilesPipeline,ImagesPipeline

我们使用时只需要,通过item的一个特殊字段将要下载文件或图片的url传递给它 们,它们会自动将文件或图片下载到本地,并将下载结果信息存入item 的另一个特殊字段,以便用户在导出文件中查阅。

首先在setting.py中启用FilesPipeline

 ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}

指定文件的下载目录

 FILES_STORE = '/home/liushuo/Download/scrapy'

在Spider解析一个包含文件下载链接的页面时,将所有需要下载文件的url地址收集到一个列表,赋给item的file_urls字 段(item['file_urls'])。FilesPipeline在处理每一项item时, 会读取item['file_urls'],对其中每一个url进行下载。

class DownloadBookSpider(scrapy.Spider):
    def parse(response):
        item = {}
        item['file_urls'] = []
        for url in response.xpath('//a/@href').extract():
            download_url = response.urljoin(url)
            item['file_urls'].append(download_url)
            yield item

当FilesPipeline下载完item['file_urls']中的所有文件后,会将各文 件的下载结果信息收集到另一个列表,赋给item的files字段 (item['files'])。下载结果信息包括以下内容: Path文件下载到本地的路径(相对于FILES_STORE的相对路 径)。 Checksum文件的校验和。 url文件的url地址。

ImagesPipeline的使用与FilesPipeline大同小异,

# settings.py

ITEM_PIPELINES = {
    'scrapy.pipelines.images.ImagesPipeline': 1,
}

IMAGES_STORE = '/path/to/your/image/directory'

# myproject/spiders/my_spider.py

import scrapy
from myproject.items import MyImageItem

class MySpider(scrapy.Spider):
    name = 'myspider'
    start_urls = ['https://example.com']

    def parse(self, response):
        item = MyImageItem()
        item['image_urls'] = response.css('img::attr(src)').extract()
        yield item

5.爬虫的重头戏---如何破解反扒机制

一、模拟登录

登录是向服务器发送含有登录表单 数据的HTTP请求(通常是POST)。Scrapy提供了一个 FormRequest类(Request的子类),专门用于构造含有表单数据的 请求,FormRequest的构造器方法有一个formdata参数,接收字典形 式的表单数据。对于表单之中的隐藏信息,可以先使用爬虫获取,或者是采用form_response的方法让scrapy帮助我们去解析。

import scrapy

class MySpider(scrapy.Spider):
    name = 'myspider'
    start_urls = ['https://example.com']

    def parse(self, response):
        # 获取表单字段值
        formdata = {
            'username': 'myusername',
            'password': 'mypassword',
        }

        # 创建 FormRequest
        yield scrapy.FormRequest(
            url='https://example.com/login',
            formdata=formdata,
            callback=self.parse_after_login
        )

    def parse_after_login(self, response):
        # 处理登录后的逻辑
        # ...

二、识别验证码

页面中的验证码图片对应一个元素,即一张图片,浏览器加 载完登录页面后,会携带之前访问获取的Cookie信息,继续发送一个 HTTP请求加载验证码图片。和账号密码输入框一样,验证码输入框也 对应一个元素,因此用户输入的验证码会成为表单数据的一部 分,表单提交后由网站服务器程序验证。

第一种破解方法,ocr识别,我们这里采用的是tesseract-ocr,安装包下载地址:Index of /tesseract,只需要下一步即可,之后我们需要在系统环境配置中去添加它的路径,这里暂时不做展开,之后我们需要pip install pillow pytesseract

之后便可以编写代码进行识别了,这种方法识别率并不是百分之一百,有失败的风险。

import cv2
import pytesseract

# 读取图像
original_image = cv2.imread('captcha.png')

# 将图像转换为灰度
grey = cv2.cvtColor(original_image, cv2.COLOR_RGB2GRAY)

# 使用 Pytesseract 识别文本
text = pytesseract.image_to_string(grey)
print("识别结果:", text)

在获取到了识别的结果候我们便可以将其返回给scrapy进行登录操作。

第二种方法:平台识别,即调用别人已经完成的识别验证码的服务

第三种方法:人工识别,检测到验证码,卡住程序,显示出验证码让人工来输入,输入结果之后继续下一步爬取

三、cookie登录

目前网站的验证 码越来越复杂,某些验证码已经复杂到人类难以识别的程度,有些时候 提交表单登录的路子难以走通。此时,我们可以换一种登录爬取的思 路,在使用浏览器登录网站后,包含用户身份信息的Cookie会被浏览 器保存在本地,如果Scrapy爬虫能直接使用浏览器中的Cookie发送 HTTP请求,就可以绕过提交表单登录的步骤。

获取浏览器的cookie,我们在这里采用的是browsercookie库来获取浏览器存储的cookie(本地),这里获取到的cookie都是我们自己的。

目前scrapy中有一个处理的cookie的中间件名为CookiesMiddleware,它会自动帮助我们处理cookie信息,但没法写入我们自己的cookie,因此我们需要重新写,

import browsercookie
     from scrapy.downloadermiddlewares.cookies import CookiesMiddleware
     class BrowserCookiesMiddleware(CookiesMiddleware):
        def __init__(self, debug=False):
            super().__init__(debug)
            self.load_browser_cookies()
        def load_browser_cookies(self):
            # 加载Chrome 浏览器中的Cookie
            jar = self.jars['chrome']
            chrome_cookiejar = browsercookie.chrome()
            for cookie in chrome_cookiejar:
                jar.set_cookie(cookie)
            # 加载Firefox 浏览器中的Cookie
            jar = self.jars['firefox']
            firefox_cookiejar = browsercookie.firefox()
            for cookie in firefox_cookiejar:
                jar.set_cookie(cookie)
相关推荐
冰帝海岸4 小时前
01-spring security认证笔记
java·笔记·spring
小二·5 小时前
java基础面试题笔记(基础篇)
java·笔记·python
朝九晚五ฺ7 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
wusong9998 小时前
mongoDB回顾笔记(一)
数据库·笔记·mongodb
猫爪笔记8 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
Resurgence038 小时前
【计组笔记】习题
笔记
pq113_69 小时前
ftdi_sio应用学习笔记 3 - GPIO
笔记·学习·ftdi_sio
澄澈i9 小时前
设计模式学习[8]---原型模式
学习·设计模式·原型模式
爱米的前端小笔记10 小时前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘
alikami10 小时前
【前端】前端学习
学习