目录
- 一、Scrapy 核心文件
- 1、spider
- 2、Request
- 3、构造 post 请求
- 4、response
- 二、案例
- 三、Scrapy 下载中间件
- 1、执行顺序
- 2、使用方法
- 3、Download Middlewares 默认方法
- 4、代理 IP
- 4.1、工作原理
- 4.2、分类
- 4.3、查看 IP 地址
- 4.4、常用代理
- 四、Scrapy 爬虫中间件
- 五、Scrapy 下载图片
- 六、Scrapy CrawlSpider 全站爬取
- 1、LinkExtractors 链接提取器
- 2、Rule 规则类
- 3、使用步骤
- 七、Scrapy 模拟登录
- 八、作业
一、Scrapy 核心文件
1、spider
- 根据 url 生成 request 对象并且指定对应的方法处理 Response,第一个 request 请求,通过起始 url 产生的。
- 解析数据,返回的 item 实例或者 request 实例。
- 在解析方法中,通常使用 Selectors(css, lxml) 提取数据。
- 返回管道完成数据的存储。
2、Request
url:请求 url,必须以字符串形式提供。
callback:响应返回的回调函数,默认是调用 parse 方法。
method:HTTP 请求方式,默认是 get 请求。
headers:请求头信息,一般在 settings.py 文件中设置。
body:data 可以采用 body 传递(使用较少)。
cookies:传递 cookies,单独设置 cookie。
meta:传参,字典形式的额外元数据,可以在请求之间传递数据,例如在不同回调之间传递参数。
encoding:响应的编码方式。
priority:设置调用请求的优先级,数值越大,优先级越高。
dont_filter:对该请求进行去重处理,默认为 False 是过滤,True 是不过滤。
errback:处理请求错误的回调函数,通常用于处理请求超时或其它错误情况。
flags:请求的标志位,可以通过添加标志位来修改请求的默认行为。
cb_kwargs:传参,字典形式的回调参数,可以在回调函数中访问这些参数。
meta 和 cb_kwargs 传参区别
meta
是用于在请求过程中在不同的中间件和回调函数之间传递数据。
它可以存储和传递更多的信息,并且可以在整个请求过程中的多个环节中使用。
是请求对象的属性,可以在整个请求过程中的多个中间件和回调函数之间共享和传递数据。
# meta
# 传值
yield scrapy.Request(url, callback=self.parse_page, meta={'category': 'books'})
# 取值
def parse_page(self, response):
category = response.meta['category']
print("Category:", category)
cb_kwargs
主要用于在请求之间传递数据给回调函数。它是通过在使用 scrapy.Request 创建请求时传递额外的参数来实现的。
是针对每个请求的回调函数的参数进行设置,它的作用范围仅限于当前请求与回调函数之间。
# cb_kwargs
# 传值
yield scrapy.Request(url, callback=self.parse_page, cb_kwargs={'title': 'Page Title'})
# 取值
def parse_page(self, response, title):
print("Page Title:", title)
3、构造 post 请求
import scrapy # 导入 Scrapy 库,用于构建爬虫
# 定义 FanyiSpider 类,继承自 scrapy.Spider 类
class FanyiSpider(scrapy.Spider):
# 爬虫的名称
name = 'fanyi'
# 允许爬取的域名
allowed_domains = ['fanyi.so.com']
# 起始 url
start_urls = ['https://fanyi.so.com/index/search?eng=1&validate=&ignore_trans=0&query=hello']
# post 请求携带参数
data = {
'eng': '1',
'ignore_trans': '0',
'query': 'hello',
}
# 定义 start_requests 方法,用于生成初始请求,重写的父类方法
def start_requests(self):
# 使用 FormRequest 发送 post 请求(FormRequest 类是继承 Request 类)
yield scrapy.FormRequest(self.start_urls[0], callback=self.parse, formdata=self.data)
# 解析函数,处理响应并提取数据
def parse(self, response):
# 打印响应数据中的翻译结果
print(response.json()['data']['fanyi'])
# 输入翻译内容
query = input("请输入翻译内容")
# post 请求携带参数
data = {
'eng': '1',
'ignore_trans': '0',
'query': query,
}
# 发送新的 post 请求并将响应交给 parse 方法处理
yield scrapy.FormRequest(self.start_urls[0], callback=self.parse, formdata=data)
4、response
一般都是由 scrapy 自动构建。
response.request.headers:获取请求头的信息
response.status:获取状态码
response.body:获取字节数据
response.text:获取文本数据
response.json:获取 json 格式的数据
response.xpath/css:解析数据
response.meta.get(key):获取传递参数
二、案例
目标网站: https://careers.tencent.com/search.html?pcid=40001
需求:翻页获取前10页数据,爬取首页的职位名字-详情页的工作职责和工作要求
# start.py
from scrapy import cmdline # 使用 cmdline 模块来执行命令行命令
# 使用 Scrapy 执行名为 spider 的爬虫
cmdline.execute('scrapy crawl spider'.split())
# spider.py
import scrapy # 导入 scrapy 库,用于构建爬虫
import json # 导入 json 库,用于处理 JSON 数据
import datetime # 导入 datetime 库,用于获取当前时间
from Tencent_Job.items import TencentJobItem # 导入自定义的数据项类 TencentJobItem
# 定义 SpiderSpider 类,继承自 scrapy.Spider 类
class SpiderSpider(scrapy.Spider):
# 爬虫的名称
name = 'spider'
# 允许爬取的域名
allowed_domains = ['careers.tencent.com']
# 获取当前时间
current_time = datetime.datetime.now()
# 将当前时间转换为时间戳字符串
post_time = str(int(current_time.timestamp()))
# 初始页码
page = 1
# 链接 url
base_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp={}&countryId=&cityId=&bgIds=&productId=&categoryId=40001001,40001002,40001003,40001004,40001005,40001006&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area='
# 起始 url
start_urls = [base_url.format(post_time,page)]
# 定义解析函数,用于解析响应数据
def parse(self, response):
# 从响应中获取数据列表
datas = json.loads(response.text)['Data']['Posts']
# 遍历数据列表
for data in datas:
# 职位名字
title = data['RecruitPostName']
# PostId
post_id = data['PostId']
# 详情页 url
post_url = 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=' + self.post_time + '&postId=' + post_id + '&language=zh-cn'
# 发送请求并指定回调函数以及传递的参数
yield scrapy.Request(post_url, callback=self.detail_pages, cb_kwargs={'title': title})
# 判断页码
if self.page < 10:
# 页码
self.page = self.page + 1
# 发送请求并指定回调函数
yield scrapy.Request(self.base_url.format(self.post_time,self.page), callback=self.parse)
# 定义职位详情页解析函数
def detail_pages(self, response, **title):
# 创建爬取的数据项
item = TencentJobItem()
# 职位名字
item['title'] = title['title']
# 工作职责
item['responsibility'] = json.loads(response.text)['Data']['Responsibility']
# 工作要求
item['requirement'] = json.loads(response.text)['Data']['Requirement']
# 返回数据项
yield item
# items.py
import scrapy # 导入Scrapy库,用于构建爬虫
# 自定义的Item类,用于存储爬取的数据
class TencentJobItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 职位名字
title = scrapy.Field()
# 工作职责
responsibility = scrapy.Field()
# 工作要求
requirement = scrapy.Field()
# pipelines.py
from itemadapter import ItemAdapter # 导入 itemadapter 库,用于适配数据项
import csv # 导入 csv 库,用于处理 CSV 文件
# 定义 TencentJobPipeline 类
class TencentJobPipeline:
# 构造函数,初始化操作
def __init__(self):
# 打开 CSV 文件,以追加模式写入,指定编码为 utf-8-sig,确保中文不乱码,设置换行符为空
self.f = open("腾讯.csv", "a", encoding='utf-8-sig', newline="")
# 定义 CSV 文件的字段名
self.fieldnames = ['title', 'responsibility', 'requirement']
# 创建 DictWriter 对象,用于写入 CSV 文件
self.writer = csv.DictWriter(self.f, fieldnames=self.fieldnames)
# 写入表头
self.writer.writeheader()
# 数据处理函数,用于处理爬取到的数据项
def process_item(self, item, spider):
# 写入数据
self.writer.writerow(item)
# 返回数据项
return item
# settings.py
# 不打印日志信息
LOG_LEVEL = 'WARNING'
# 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; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'Tencent_Job.pipelines.TencentJobPipeline': 300,
}
三、Scrapy 下载中间件
在 Scrapy 中,下载中间件(Download Middleware)是一种机制,用于在发送请求和获取响应之间对请求和响应进行处理和操作。下载中间件允许在整个请求-响应周期中拦截、修改和生成请求,以及拦截、修改和生成响应。
下载中间件主要用于以下几个方面:
- 请求处理:下载中间件可以拦截并修改将要发送的请求。可以在请求被发送到服务器之前对其进行预处理,例如添加自定义的请求头、修改请求参数或 url 等。
- 代理和用户代理设置:通过下载中间件,可以轻松地为请求设置代理服务器,以及设置自定义的用户代理(User-Agent)。这对于处理需要使用代理或模拟不同浏览器的场景非常有用。
- 请求过滤和调度优化:下载中间件可以用于对请求进行过滤和调度优化。可以根据特定的规则或条件过滤掉某些请求,或者对请求进行调度优化,例如动态地设置请求的优先级、延迟等。
- 响应处理:下载中间件可以拦截并修改从服务器返回的响应。可以对响应进行预处理、提取数据、添加额外的数据等。还可以根据响应的状态码进行处理,例如重试、错误处理等。
1、执行顺序
要使用下载中间件,需要在 Scrapy 项目的设置文件(settings.py)中进行配置。
可以配置多个下载中间件,并按顺序定义它们的优先级。
Scrapy 会按照配置的顺序依次调用下载中间件,直到最后一个中间件完成处理或返回响应。
中间件是多个,数字越小,越靠近引擎,数字越大,越靠近下载器。
处理请求时,数字越小,优先级越高,
处理响应时,数字越大,优先级越高。
2、使用方法
编写一个 Download Middlewares,定义一个类,然后在 settings 中开启。
# middlewares.py
# 自定义随机生成 UA 的中间件
class RandomUserAgentMiddewar:
def __init__(self):
# 初始化 user_agent 列表,包含多个浏览器的 User-Agent 字符串
self.user_agent = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
]
# 重写 process_request 方法
# 处理请求,引擎发送 request 请求到下载器之前,会调用这个方法。
# 启用中间件,要去 settigns.py 文件中开启。
def process_request(self, request, spider):
# 在请求头中设置随机选择的 User-Agent
request.headers['user-agent'] = random.choice(self.user_agent)
# settings.py
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
# 'middle.middlewares.MiddleDownloaderMiddleware': 543,
'middle.middlewares.RandomUserAgentMiddewar': 543,
}
3、Download Middlewares 默认方法
# middlewares.py
class TencentJobDownloaderMiddleware:
# 对请求进行处理和修改
# 处理请求,引擎发送 request 请求到下载器之前,会调用这个方法
# 启用中间件,要去 settigns.py 文件中开启
def process_request(self, request, spider):
# request:请求对象,spider:爬虫对象
# Called for each request that goes through the downloader
# middleware.
# 按照需求和处理逻辑,返回以下几种结果之一
# Must either:
# 会继续处理下一个请求,直到下载把 request 得到 response 才会结束
# - return None: continue processing this request
# 不继续处理请求,会进入到最后一个中间件,调用 process_response,直接处理响应对象
# - or return a Response object
# 会重新进入到第一个中间件 process_request 重新处理请求
# - or return a Request object
# 触发异常
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None
# 对响应进行处理和修改
# 每个响应经过下载中间件时都会调用该方法
def process_response(self, request, response, spider):
# request:请求对象,response:响应对象,spider:爬虫对象
# Called with the response returned from the downloader.
# 按照需求和处理逻辑,返回以下几种结果之一
# Must either;
# 表示对响应进行处理和修改后,将修改后的响应返回,后续的下载中间件和爬虫将使用该修改后的响应进行进一步处理
# - return a Response object
# 表示对响应进行处理后,生成一个新的请求对象,并将其返回,该新的请求对象将被发送到下载器进行处理
# - return a Request object
# 表示忽略该响应,将不再调用其它下载中间件的 process_response 方法,并继续执行已安装的其它下载中间件的 process_exception 方法。
# - or raise IgnoreRequest
return response
# 处理请求过程中的异常
def process_exception(self, request, exception, spider):
# request:请求对象,exception:引发的异常对象,spider:爬虫对象
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.
# 按照需求和处理逻辑,返回以下几种结果之一
# Must either:
# 表示继续处理当前的异常,即继续执行后续的下载中间件的 process_exception 方法
# - return None: continue processing this exception
# 表示停止处理异常,并将该响应对象作为最终的结果返回,后续的下载中间件和爬虫将使用该响应对象进行进一步处理
# - return a Response object: stops process_exception() chain
# 表示停止处理异常,并将该请求对象作为最终的结果返回,该新的请求对象将被重新发送到下载器进行处理
# - return a Request object: stops process_exception() chain
pass
4、代理 IP
代理 IP(Proxy IP)是一种通过中间服务器转发网络请求的方式,隐藏真实客户端的 IP 地址并代表客户端进行网络通信。使用代理 IP 可以实现一些特定的目标,例如绕过网络封锁、提高网络访问速度、隐藏真实身份等。
4.1、工作原理
客户端发送网络请求时,将请求发送给代理服务器而不是直接发送给目标服务器。
代理服务器接收到请求后,替代客户端向目标服务器发送请求,并将响应返回给客户端。
目标服务器将响应发送给代理服务器,代理服务器再将响应转发给客户端。
4.2、分类
透明代理(Transparent Proxy)
代理服务器会将客户端的真实 IP 地址传递给目标服务器,目标服务器可以直接获得客户端的真实 IP。
匿名代理(Anonymous Proxy)
代理服务器会隐藏客户端的真实 IP 地址,并使用自己的 IP 地址作为代理发送请求,但目标服务器仍然可以检测到使用了代理。
高匿代理(Elite Proxy)
代理服务器完全隐藏客户端的真实 IP 地址,并使用自己的 IP 地址作为代理发送请求,目标服务器无法检测到使用了代理。
4.3、查看 IP 地址
在终端输入 ipconfig 命令——内网 IP,私有地址
在浏览器地址栏输入 ipip.net——外网,上网的 IP
4.4、常用代理
快代理、豌豆代理
四、Scrapy 爬虫中间件
爬虫中间件(Spider Middleware)是 Scrapy 中的一种中间件,位于引擎和爬虫之间,主要处理发生给爬虫的响应和 spider 输出结果。中间件可以是多个,数字越小,越靠近引擎,数字越大,越靠近爬虫。
使用爬虫中间件,需要在 settings.py 文件里开启爬虫中间件。
# settings.py
# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
SPIDER_MIDDLEWARES = {
'myproject.middlewares.CustomSpiderMiddleware': 543,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}
五、Scrapy 下载图片
Scrapy 为下载 item 中包含的文件提供了一个可重用的 item pipelines,这些 pipeline 有些共同的方法和结构,常用的有 Files Pipline 和 Images Pipeline。
Images Pipeline 使用步骤
1、先创建项目。
在终端输入命令
scrapy startproject BiaoQingBao
cd BiaoQingBao
scrapy genspider images fabiaoqing.com
2、在 settings.py 文件中修改一些参数。
# settings.py
# 不打印日志信息
LOG_LEVEL = 'WARNING'
# 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; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
3、新建一个 start.py 用来运行代码。
# start.py
# 使用 cmdline 模块来执行命令行命令
from scrapy import cmdline
# 使用 Scrapy 执行名为 images 的爬虫
cmdline.execute('scrapy crawl images'.split())
4、在 items.py 定义字段。
# items.py
import scrapy # 导入 Scrapy 库,用于构建爬虫
class BiaoqingbaoItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 图片链接
img_url = scrapy.Field()
# 图片标题
img_title = scrapy.Field()
pass
5、在 images.py 里做数据解析。
# images.py
import scrapy # 导入 Scrapy 库,用于构建爬虫
import re # 导入 re 模块,用于进行正则表达式匹配
from BiaoQingBao.items import BiaoqingbaoItem # 导入自定义的 Item 类,用于存储爬取的数据
class SpiderSpider(scrapy.Spider):
# 爬虫的名称
name = 'images'
# 允许爬取的域名
# allowed_domains = ['fabiaoqing.com']
# 链接 url
base_url = 'https://fabiaoqing.com/biaoqing/lists/page/{}.html'
# 页码
page = 1
# 起始 url
start_urls = [base_url.format(page)]
def parse(self, response):
# 解析数据,找所有的 img 标签
images = response.xpath('//img[@class="ui image lazy"]')
# 遍历获取每一个 img 标签,解析里面的图片 url 以及标题
for img in images:
# 创建一个 MyScrapyItem 实例,用于存储爬取的数据
item = BiaoqingbaoItem()
# 图片 url
item['img_url'] = img.xpath('@data-original').get()
# 标题
title = img.xpath('@title').get()
# 正则表达式替换标题特殊字符
item['img_title'] = re.sub(r'[?/\\<>*:(), ]', '', title)
# 返回 item,将其传递给引擎
yield item
# 进行翻页处理
if self.page <= 10:
self.page += 1
# 获取数据
yield scrapy.Request(self.base_url.format(self.page), callback=self.parse)
6、如果爬取的速度过快,会被服务器识别是一个程序,可以设置一下爬取的速度。
在 settings.py 文件里找到以下代码,取消注释。
# settings.py
# 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 = 0.5
7、在 pipelines.py 里做下载图片并处理相关的请求。
# pipelines.py
import scrapy # 导入 Scrapy 库,用于构建爬虫
from itemadapter import ItemAdapter # 导入 itemadapter 模块中的 ItemAdapter 类,用于简化和处理爬虫中的数据项
from scrapy.pipelines.images import ImagesPipeline # 导入 scrapy.pipelines.images 模块中的 ImagesPipeline 类,用于处理爬取的图片数据
# 定义 BiaoqingbaoPipeline 类,继承自 ImagesPipeline 类
class BiaoqingbaoPipeline(ImagesPipeline):
# def process_item(self, item, spider):
# return item
# 专门下载图片的方法
def get_media_requests(self, item, info):
# 获取图片 url
img_url = item['img_url']
# 发起图片下载请求
yield scrapy.Request(img_url)
8、在 settings.py 中指定保存的路径并开启管道。
# settings.py
# 指定保存的路径
IMAGES_STORE = 'F:\Scrapy\BiaoQingBao\images'
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'BiaoQingBao.pipelines.BiaoqingbaoPipeline': 300,
}
在 start.py 运行代码
WARNING: File (code: 403): Error downloading file from <GET https://img.soutula.com/large/ceeb653ely8heu3ipe30zg20af0dw4pp.gif> referred in
出现错误 403,说明被服务器识别出是爬虫程序了。
保存图片时出现反爬,检查图片的来源。
9、修改 pipelines.py 文件,携带请求头。
# pipelines.py
import scrapy # 导入 Scrapy 库,用于构建爬虫
from itemadapter import ItemAdapter # 导入 itemadapter 模块中的 ItemAdapter 类,用于简化和处理爬虫中的数据项
from scrapy.pipelines.images import ImagesPipeline # 导入 scrapy.pipelines.images 模块中的 ImagesPipeline 类,用于处理爬取的图片数据
# 定义 BiaoqingbaoPipeline 类,继承自 ImagesPipeline 类
class BiaoqingbaoPipeline(ImagesPipeline):
# def process_item(self, item, spider):
# return item
# 请求头
head = {
'Referer': 'https://fabiaoqing.com/'
}
# 专门下载图片的方法
def get_media_requests(self, item, info):
# 获取图片 url
img_url = item['img_url']
# 发起图片下载请求
yield scrapy.Request(img_url, headers=self.head)
10、保存图片的缺点是,文件名不方便看,需要使用 file_path 重新给图片命名。
# pipelines.py
import scrapy # 导入 Scrapy 库,用于构建爬虫
from itemadapter import ItemAdapter # 导入 itemadapter 模块中的 ItemAdapter 类,用于简化和处理爬虫中的数据项
from scrapy.pipelines.images import ImagesPipeline # 导入 scrapy.pipelines.images 模块中的 ImagesPipeline 类,用于处理爬取的图片数据
# 定义 BiaoqingbaoPipeline 类,继承自 ImagesPipeline 类
class BiaoqingbaoPipeline(ImagesPipeline):
# def process_item(self, item, spider):
# return item
# 请求头
head = {
'Referer': 'https://fabiaoqing.com/'
}
# 专门下载图片的方法
def get_media_requests(self, item, info):
# 获取图片 url
img_url = item['img_url']
# 发起图片下载请求
yield scrapy.Request(img_url, headers=self.head)
# 重新给图片命名
def file_path(self, request, response=None, info=None, *, item=None):
# 名称
img_name = item['img_title']
# 指定保存文件夹
return f'full/{img_name}.jpg'
注意:
错误信息:ImagesPipeline requires installing Pillow 4.0.0 or later
解决方案:pip install Pillow
六、Scrapy CrawlSpider 全站爬取
Scrapy 的 CrawlSpider 是一个高级的 Spider 类,用于处理常见的基于规则的爬取任务。CrawlSpider 扩展了 Scrapy 的 Spider 类,并提供了一些方便的功能,如自动跟进链接、规则匹配和提取等。是基于全站数据的爬取,将所有的数据爬取下来。
1、LinkExtractors 链接提取器
使用 LinkExtractors 可以不用自己提取想要的 url,然后发送请求。这些都可以交给 LinkExtractors,它会在所有页面中找到满足规则的 url,实现自动的爬取。
class scrapy.linkextractors.LinkExtractor(
allow = (),
deny = (),
allow_domains = (),
deny_domains = (),
deny_extensions = None,
restrict_xpaths = (),
tags = ('a','area'),
attrs = ('href'),
canonicalize = True,
unique = True,
process_value = None
)
主要参数讲解
allow:允许的 url,所有满足这个正则表达式的 url 都会被提取。
deny:禁止的 url,所有满足这个正则表达式的 url 都不会被提取。
allow_domains:允许的域名,只有在这个里面指定的域名的 url 才会被提取。
deny_domains:禁止的域名,所有在这个里面指定的域名的 url 都不会被提取。
restrict_xpaths:严格的 xpath,和 allow 共同过滤链接。
2、Rule 规则类
定义爬虫的规则类。
class scrapy.spiders.Rule(
link_extractor,
callback = None,
cb_kwargs = None,
follow = None,
process_links = None,
process_request = None
)
主要参数讲解
link_extractor:一个 LinkExtractor 对象,用于定义爬取规则。
callback:满足这个规则的 url,应该要执行的回调函数。因为 CrawlSpider 使用了 parse 作为回调函数,因此不要覆盖 parse 作为回调函数自己的回调函数。
follow:指定根据该规则从 response 中提取的链接是否需要跟进。
process_links:从 link_extractor 中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接。
3、使用步骤
1、创建项目。
scrapy startproject 项目名
cd 项目名
scrapy genspider -t crawl 爬虫文件名 域名
imgs_names.py 文件代码
# imgs_names.py
import scrapy # 导入 Scrapy 库,用于构建爬虫
from scrapy.linkextractors import LinkExtractor # 导入 scrapy.linkextractors 模块中的 LinkExtractor 类,用于从网页中提取 url 链接
from scrapy.spiders import CrawlSpider, Rule # 导入 scrapy.spiders 模块中的 CrawlSpider 类和 Rule 类,用于实现基于规则的爬取方式和定义爬取规则
# 定义 ImgsNamesSpider 类,继承自 CrawlSpider 类
class ImgsNamesSpider(CrawlSpider):
# 爬虫的名称
name = 'imgs_names'
# 允许爬取的域名
allowed_domains = ['baidu.com']
# 设置起始 url 列表
start_urls = ['http://baidu.com/']
# 创建一个规则对象
rules = (
# LinkExtractor:实例化了对象
# allow:提取 url 的正则表达式
# callback:回调函数,提取的 url 生成一个 requests 请求,请求发送给引擎,指定数据解析的方法
# follow:指定根据规则从 requests 提取的链接是否继续提取
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
# 解析方法,用于处理爬取到的响应数据
def parse_item(self, response):
# 创建空的数据项字典
item = {}
# 解析数据项的域名 id
# item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
# 解析数据项的名称
# item['name'] = response.xpath('//div[@id="name"]').get()
# 解析数据项的描述
# item['description'] = response.xpath('//div[@id="description"]').get()
# 返回数据项
return item
2、在 settings.py 文件中修改一些参数。
# settings.py
# 不打印日志信息
LOG_LEVEL = 'WARNING'
# 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; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
# 设置爬取时间
DOWNLOAD_DELAY = 0.5
3、新建一个 start.py 用来运行代码。
# start.py
# 使用 cmdline 模块来执行命令行命令
from scrapy import cmdline
# 使用 Scrapy 执行名为 imgs_names 的爬虫
cmdline.execute('scrapy crawl imgs_names'.split())
4、在 imgs_names.py 中获取全站数据。
# imgs_names.py
import scrapy # 导入 Scrapy 库,用于构建爬虫
from scrapy.linkextractors import LinkExtractor # 导入 scrapy.linkextractors 模块中的 LinkExtractor 类,用于从网页中提取 url 链接
from scrapy.spiders import CrawlSpider, Rule # 导入 scrapy.spiders 模块中的 CrawlSpider 类和 Rule 类,用于实现基于规则的爬取方式和定义爬取规则
# 定义 ImgsNamesSpider 类,继承自 CrawlSpider 类
class ImgsNamesSpider(CrawlSpider):
# 爬虫的名称
name = 'imgs_names'
# 允许爬取的域名
# allowed_domains = ['fabiaoqing.com']
# 设置起始 url 列表
start_urls = ['https://fabiaoqing.com/biaoqing/lists/page/1.html']
# 创建一个规则对象
rules = (
# 定义规则列表
Rule(LinkExtractor(allow=r'/biaoqing/lists/page/\d+.html'), callback='parse_item', follow=True),
)
# 解析方法,用于处理爬取到的响应数据
def parse_item(self, response):
# 创建空的数据项字典
item = {}
# 直接获取整个页面图片标题,返回的数据类型是 list
lst = response.xpath('//img[@class="ui image lazy"]/@title').getall()
# 打印图片标题
print(lst)
# 返回数据项
return item
七、Scrapy 模拟登录
1、创建项目。
scrapy startproject login
cd login
scrapy genspider example example.com
2、新建一个 start.py 用来运行代码。
# start.py
# 使用 cmdline 模块来执行命令行命令
from scrapy import cmdline
# 使用 Scrapy 执行名为 example 的爬虫
cmdline.execute('scrapy crawl example'.split())
3、在 spiders 文件夹下的 example.py 文件中编写代码。
# spiders/example.py
import scrapy # 导入 Scrapy 库
class ExampleSpider(scrapy.Spider):
name = 'example' # 爬虫的名字,用于启动爬虫时的标识
allowed_domains = ['qq.com'] # 允许爬取的域名
start_urls = ['https://user.qzone.qq.com/1234567890'] # 起始 URL,爬虫会从这个 URL 开始爬取数据
def parse(self, response): # 解析函数,用于处理响应数据
print(response.text) # 打印响应的 HTML 文本内容
4、设置 cookie
4.1、方式一:直接携带 cookie 登录
在 settings.py 文件中修改一些参数;
# settings.py
# 不打印日志信息
LOG_LEVEL = 'WARNING'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
'Cookie':'_qpsvr_localtk=0.6206021496459884; uin=o1234567890; skey=@kalEm2dQT; RK=2r/cU4cJEh; ptcz=8235ccaac75bae8201f47a835b216cd97d8f530349b6865815d301b699dec7c5; p_uin=o1234567890; pt4_token=VNY2rJ1KBn7v9iHp1eFMfaRrxmzuEogpZimR20ookP4_; p_skey=C03fYXYvuuIISyOKcJeMrd6nKddc0iNkh-9p-VkMg7U_; Loading=Yes; welcomeflash=1234567890_58028; qz_screen=1707x960; 1234567890_todaycount=0; 1234567890_totalcount=711; pgv_pvid=5798017608; pgv_info=ssid=s9552383352; QZ_FE_WEBP_SUPPORT=1; scstat=18; cpu_performance_v8=2; __Q_w_s__QZN_TodoMsgCnt=1',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.3',
}
# Disable cookies (enabled by default)
COOKIES_ENABLED = False
注意:
在 settings.py 直接携带 cookie 是不生效,还需要将 COOKIES_ENABLED = False 注释取消掉。
4.2、方式二:在中间件设置 cookie
可以直接在 proces_request 的方法上直接修改,也可以自定义⼀个中间件类,但里面的方法名称不能随便修改。
# middlewares.py
# 自定义中间件
class CookieDownloaderMiddleware:
def process_request(self, request, spider):
# 把 cookie 添加到请求里面,格式要求必须是字典格式的数据
cookie_str = "_qpsvr_localtk=0.6206021496459884; uin=o1234567890; skey=@kalEm2dQT; RK=2r/cU4cJEh; ptcz=8235ccaac75bae8201f47a835b216cd97d8f530349b6865815d301b699dec7c5; p_uin=o1234567890; pt4_token=VNY2rJ1KBn7v9iHp1eFMfaRrxmzuEogpZimR20ookP4_; p_skey=C03fYXYvuuIISyOKcJeMrd6nKddc0iNkh-9p-VkMg7U_; Loading=Yes; welcomeflash=1234567890_58028; qz_screen=1707x960; 1234567890_todaycount=0; 1234567890_totalcount=711; pgv_pvid=5798017608; pgv_info=ssid=s9552383352; QZ_FE_WEBP_SUPPORT=1; scstat=18; __Q_w_s__QZN_TodoMsgCnt=1; cpu_performance_v8=25"
cookie_dict = {i.split("=")[0]: i.split("=")[1] for i in cookie_str.split("; ")}
request.cookies = cookie_dict
return None
# settings.py
# 不打印日志信息
LOG_LEVEL = 'WARNING'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Disable cookies (enabled by default)
COOKIES_ENABLED = True
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
'login.middlewares.LoginDownloaderMiddleware': 543,
'login.middlewares.CookieDownloaderMiddleware': 541,
}
- 当 COOKIES_ENABLED 是注释的时候 scrapy 默认没有开启 cookie。
- 当 COOKIES_ENABLED 没有注释,设置为 False 的时候 scrapy 默认使用了 settings 里面的 cookie。
- 当 COOKIES_ENABLED 设置为 True 的时候 scrapy 就会把 settings 的 cookie 关掉,使用自定义 cookie。
4.3、方式三:爬虫文件中,重写 start_rquests 方法
# spiders/example.py
import scrapy # 导入 Scrapy 库
class QzoneSpider(scrapy.Spider):
name = 'example' # 爬虫的名字,用于启动爬虫时的标识
allowed_domains = ['qq.com'] # 允许爬取的域名
start_urls = ['https://user.qzone.qq.com/1234567890'] # 起始 URL,爬虫会从这个 URL 开始爬取数据
cookie_str = "_qpsvr_localtk=0.6206021496459884; uin=o1234567890; skey=@kalEm2dQT; RK=2r/cU4cJEh; ptcz=8235ccaac75bae8201f47a835b216cd97d8f530349b6865815d301b699dec7c5; p_uin=o1234567890; pt4_token=VNY2rJ1KBn7v9iHp1eFMfaRrxmzuEogpZimR20ookP4_; p_skey=C03fYXYvuuIISyOKcJeMrd6nKddc0iNkh-9p-VkMg7U_; Loading=Yes; welcomeflash=1234567890_58028; qz_screen=1707x960; 1234567890_todaycount=0; 1234567890_totalcount=711; pgv_pvid=5798017608; pgv_info=ssid=s9552383352; QZ_FE_WEBP_SUPPORT=1; scstat=18; __Q_w_s__QZN_TodoMsgCnt=1; cpu_performance_v8=25"
def start_requests(self):
# 解析 cookie 字符串,将其转换为字典形式
cookie_dict = {i.split("=")[0]: i.split("=")[1] for i in self.cookie_str.split("; ")}
yield scrapy.Request(
url=self.start_urls[0], # 使用第一个起始 URL
callback=self.parse, # 响应后交给 parse 方法处理
cookies=cookie_dict # 设置请求的 Cookie
)
def parse(self, response):
print(response.text) # 打印响应的 HTML 文本内容
pass # 占位符,表示这个函数暂时不做任何处理,可以根据需要添加解析逻辑
八、作业
目标网站:https://github.com/login
需求:以抓包的形式完成模拟登录
1、创建项目
scrapy startproject github_login
cd github_login
scrapy genspider github github.com
2、新建一个 start.py 用来运行代码。
# start.py
# 使用 cmdline 模块来执行命令行命令
from scrapy import cmdline
# 使用 Scrapy 执行名为 github_login 的爬虫
cmdline.execute('scrapy crawl github'.split())
3、在 settings.py 文件中修改一些参数。
# settings.py
# 不打印日志信息
LOG_LEVEL = 'WARNING'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
4、抓包分析 POST 数据
多次输入错误的用户名和密码,看 POST 请求数据,哪些是变化的,哪些是不变的
5、在 spiders 文件夹下的 github.py 文件中编写代码。
# spiders/github.py
import scrapy # 导入 Scrapy 库
# 定义一个名为 GithubSpider 的 Spider 类,继承自 Scrapy 的 Spider 类
class GithubSpider(scrapy.Spider):
name = 'github' # 设置 Spider 的名字为'github'
allowed_domains = ['github.com'] # 允许爬取的域名
start_urls = ['https://github.com/login'] # 起始 URL,即登录页面的 URL
# 定义一个名为 parse 的方法,用于处理登录页面的响应
def parse(self, response):
# 提取 authenticity_token、timestamp 和 timestamp_secret 参数值
authenticity_token = response.css('input[name="authenticity_token"]::attr(value)').get()
timestamp = response.css('input[name="timestamp"]::attr(value)').get()
timestamp_secret = response.css('input[name="timestamp_secret"]::attr(value)').get()
# 构造登录请求的 POST 数据
data_dict = {
'commit': 'Sign in', # 提交按钮的值
'authenticity_token': authenticity_token, # 登录页面中的 authenticity_token 值
'login': 'XXX', # GitHub 用户名
'password': 'XXX', # GitHub 密码
'webauthn-support': 'supported', # WebAuthn 支持情况
'webauthn-iuvpaa-support': 'unsupported', # WebAuthn 支持情况
'return_to': 'https://github.com/login', # 返回到登录页面
'timestamp': timestamp, # 时间戳
'timestamp_secret': timestamp_secret # 时间戳密钥
}
# 构造登录请求
yield scrapy.FormRequest.from_response(
response,
formdata=data_dict, # POST 数据字典
callback=self.after_login # 登录成功后处理响应的回调函数
)
# 定义一个名为 after_login 的方法,用于处理登录后的响应
def after_login(self, response):
if response.status == 200: # 如果响应的状态码为200(HTTP成功),表示登录成功
print('登录成功!') # 打印登录成功的消息
else: # 如果响应状态码不是200,表示登录失败
print('登录失败!') # 打印登录失败的消息
记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~