2025年3月27日 星期四 甲辰(龙)年 月廿六 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

Python爬虫Scrapy轮子工具

时间:04-30来源:作者:点击数:50

概念

scrapy框架的运行流程以及数据传递过程:

  1. 爬虫中起始的url构造成request对象–>爬虫中间件–>引擎–>调度器
  2. 调度器把request–>引擎–>下载中间件—>下载器
  3. 下载器发送请求,获取response响应---->下载中间件---->引擎—>爬虫中间件—>爬虫
  4. 爬虫提取url地址,组装成request对象---->爬虫中间件—>引擎—>调度器,重复步骤2
  5. 爬虫提取数据—>引擎—>管道处理和保存数据

模块作用

引擎(engine):负责数据和信号在不腰痛模块间的传递

调度器(scheduler):实现一个队列,存放引擎发过来的request请求对象

下载器(downloader):发送引擎发过来的request请求,获取响应,并将响应交给引擎

爬虫(spider):处理引擎发过来的response,提取数据,提取url,并交给引擎

管道(pipeline):处理引擎传递过来的数据,比如存储

下载中间件(downloader middleware):可以自定义的下载扩展,比如设置代理ip

爬虫中间件(spider middleware):可以自定义request请求和进行response过滤,与下载中间件作用重复

开发步骤

  1. 创建项目:
    scrapy startproject mySpider
    scrapy startproject <项目名字>
  2. 生成一个爬虫【在项目目录下执行】:
    scrapy genspider lianjia lianjia.com
    scrapy genspider <爬虫名字> <允许爬取的域名>
    爬虫名字: 作为爬虫运行时的参数
    允许爬取的域名: 为对于爬虫设置的爬取范围,设置之后用于过滤要爬取的url,如果爬取的url与允许的域不通则被过滤掉。
  3. 提取数据:
    修改start_urls
    检查修改allowed_domains
    编写解析方法
    根据网站结构在spider中实现数据采集相关内容
  4. 保存数据:
    在pipelines.py文件中定义对数据处理的管道
    在settings.py文件中注册启用管道
  5. 启动项目

爬虫文件 spiders.py

代码流程

  1. 完善并使用Item数据类:
  2. 在items.py中完善要爬取的字段
  3. 在爬虫文件中先导入Item
  4. 实力化Item对象后,像字典一样直接使用
  5. 构造Request对象,并发送请求:
  6. 导入scrapy.Request类
  7. 在解析函数中提取url
  8. yield scrapy.Request(url, callback=self.parse_detail, meta={})
  9. 利用meta参数在不同的解析函数中传递数据:
  10. 通过前一个解析函数 yield scrapy.Request(url, callback=self.xxx, meta={}) 来传递meta
  11. 在self.xxx函数中 response.meta.get(‘key’, ‘’) 或 response.meta[‘key’] 的方式取出传递的数据

提取数据

  • 解析并获取scrapy爬虫中的数据:
  • 1. response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
  • 2. extract() 返回一个包含有字符串的列表,配合[0]使用
  • 3. extract_first() 返回列表中的第一个字符串,列表为空没有返回None
  • 4. get() 提取列表中第1个文本内容(等同于extract_first())

scrapy.Request发送post请求

  • 我们知道可以通过scrapy.Request()指定methodbody参数来发送post请求;但是通常使用scrapy.FormRequest()来发送post请求

发送post请求

注意:scrapy.FormRequest()能够发送表单和ajax请求,参考阅读 https://www.cdsy.xyz/computer/programme/Python/230430/cd43347.html

response响应

response.url:当前响应的url地址

  • 1. response.request.url:当前响应对应的请求的url地址
  • 2. response.headers:响应头
  • 3. response.requests.headers:当前响应的请求头
  • 4. response.body:响应体,也就是html代码,byte类型
  • 5. response.status:响应状态码

Cookie

  • 1.在settings.py中通过设置COOKIES_DEBUG=TRUE 能够在终端看到cookie的传递传递过程
  • 2.如果start_url地址中的url是需要登录后才能访问的url地址,则需要重写3.start_request方法并在其中手动添加上cookie
  • 4.如果重新start_requests()方法,则需要去掉start_urls变量

参数解释

  1. 中括号里的参数为可选参数
  2. callback:表示当前的url的响应交给哪个函数去处理
  3. meta:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延迟,请求深度等
  4. dont_filter:默认为False,会过滤请求的url地址,即请求过的url地址不会继续被请求,对需要重复请求的url地址可以把它设置为Ture,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动
  5. method:指定POST或GET请求
  6. headers:接收一个字典,其中不包括cookies
  7. cookies:接收一个字典,专门放置cookies
  8. body:接收json字符串,为POST的数据,发送payload_post请求时使用(在下一章节中会介绍post请求)
  • 注意:解析函数中的yield能够传递的对象只能是:BaseItem, Request, dict, None

meta参数的使用

  • meta的作用:meta可以实现数据在不同的解析函数中的传递

在爬虫文件的parse方法中,提取详情页增加之前callback指定的parse_detail函数:

  • def parse(self,response):
  • ...
  • yield scrapy.Request(detail_url, callback=self.parse_detail,meta={"item":item})
  • ...
  • def parse_detail(self,response):
  • #获取之前传入的item
  • item = resposne.meta["item"]

注意

1. meta参数是一个字典

2. meta字典中有一个固定的键proxy,表示代理ip,关于代理ip的使用我们将在scrapy的下载中间件

管道 piplines.py

管道的基本使用:

1. 完善pipelines.py中的process_item函数

2. 在settings.py中设置开启pipeline

3. pipeline在setting中键表示位置(即pipeline在项目中的位置可以自定义),值表示距离引擎的远近,越近数据会越先经过:权重值小的优先执行

4. 1. 有多个pipeline的时候,process_item的方法必须return item,否则后一个pipeline取到的数据为None值

5. pipeline中process_item的方法必须有,否则item没有办法接受和处理

6. process_item方法接受item和spider,其中spider表示当前传递item过来的spider

pipeline中常用的方法:

  1. process_item(self,item,spider):
    • 管道类中必须有的函数
    • 实现对item数据的处理
    • 必须return item
  2. open_spider(self, spider): 在爬虫开启的时候仅执行一次
  3. close_spider(self, spider): 在爬虫关闭的时候仅执行一次

开启管道

  • 在settings.py设置开启pipeline
  • ITEM_PIPELINES = {
  • 'myspider.pipelines.ItcastFilePipeline': 400, # 400表示权重
  • 'myspider.pipelines.ItcastMongoPipeline': 500, # 权重值越小,越优先执行!
  • }

多个管道的开启作用

  • 1. 不同的pipeline可以处理不同爬虫的数据,通过spider.name属性来区分
  • 2. 不同的pipeline能够对一个或多个爬虫进行不同的数据处理的操作,比如一个进行数据清洗,一个进行数据的保存
  • 3. 同一个管道类也可以处理不同爬虫的数据,通过spider.name属性来区分

MongoDB 持久化

  • # 【1】settings.py中添加
  • ITEM_PIPELINES = {
  • # 添加MongoDB管道
  • 'Tencent.pipelines.TencentMongoPipeline': 400,
  • }
  • MONGO_HOST = '127.0.0.1'
  • MONGO_PORT = 27017
  • MONGO_DB = 'tencentdb'
  • MONGO_SET = 'tencentset'
  • # 【2】pipelines.py中新建MongoDB管道类
  • from .settings import *
  • import pymongo
  • class TencentMongoPipeline:
  • def open_spider(self, spider):
  • self.conn = pymongo.MongoClient(MONGO_HOST, MONGO_PORT)
  • self.db = self.conn[MONGO_DB]
  • self.myset = self.db[MONGO_SET]
  • def process_item(self, item, spider):
  • self.myset.insert_one(dict(item))

Json 持久化

  • import json
  • class WangyiPipeline(object):
  • def open_spider(self, spider):
  • if spider.name == 'job':
  • self.file = open('wangyi.json', 'w')
  • def process_item(self, item, spider):
  • if spider.name == 'job':
  • item = dict(item)
  • str_data = json.dumps(item, ensure_ascii=False) + ',\n'
  • self.file.write(str_data)
  • return item
  • def close_spider(self, spider):
  • if spider.name == 'job':
  • self.file.close()

MySql 持久化

1)MySQL建表

  • create database tencentdb charset utf8;
  • use tencentdb;
  • create table tencenttab(
  • job_name varchar(200),
  • job_type varchar(200),
  • job_duty varchar(2000),
  • job_require varchar(2000),
  • job_add varchar(100),
  • job_time varchar(100)
  • )charset=utf8;

2)MySql数据持久化

  • # 【1】settings.py添加
  • ITEM_PIPELINES = {
  • # 在原来基础上添加MySQL的管道
  • 'Tencent.pipelines.TencentMysqlPipeline': 200,
  • }
  • MYSQL_HOST = '127.0.0.1'
  • MYSQL_USER = 'root'
  • MYSQL_PWD = '123456'
  • MYSQL_DB = 'tencentdb'
  • CHARSET = 'utf8'
  • # 【2】pipelines.py新建MySQL管道类
  • from .settings import *
  • import pymysql
  • class TencentMysqlPipeline:
  • def open_spider(self, spider):
  • self.db = pymysql.connect(MYSQL_HOST, MYSQL_USER, MYSQL_PWD, MYSQL_DB, charset=CHARSET)
  • self.cur = self.db.cursor()
  • self.ins = 'insert into tencenttab values(%s,%s,%s,%s,%s,%s)'
  • def process_item(self, item, spider):
  • li = [
  • item['job_name'],
  • item['job_type'],
  • item['job_duty'],
  • item['job_require'],
  • item['job_add'],
  • item['job_time'],
  • ]
  • self.cur.execute(self.ins, li)
  • self.db.commit()
  • return item
  • def close_spider(self, item, spider):
  • self.cur.close()
  • self.db.close()
  • 进阶:mysql连接池

Csv持久化

  • piplines.py
  • import csv
  • class LianjiaspiderPipeline(object):
  • def open_spider(self, spider):
  • if spider.name == "home":
  • self.file = open('lianjia.csv', 'w', encoding="utf-8")
  • self.f_csv = csv.writer(self.file)
  • headers = ['地址', '总价', '单价', '户型', '面积', "朝向", "楼层", "装修程度", "详情页url"]
  • self.f_csv.writerow(headers)
  • def process_item(self, item, spider):
  • if spider.name == "home":
  • data = [item["address"], item["price"], item["unit_price"], item["door_model"], item["area"],
  • item["direction"], item["floor"], item["decorate"], item["info_url"]]
  • self.f_csv.writerow(data)
  • print(data)
  • return item
  • def close_spider(self, spider):
  • if spider.name == "home":
  • self.file.close()
  • settings.py
  • # 管道
  • ITEM_PIPELINES = {
  • 'LianJiaSpider.pipelines.LianjiaspiderPipeline': 300,
  • }

中间件 middleware.py

中间件的分类

  • 1.下载器中间件
  • 2.爬虫中间件

Scrapy中间的作用(预处理Request和Response对象)

  • 1. 对header以及cookie进行更换和处理
  • 2. 使用代理ip等
  • 3. 对请求进行定制化操作,
  • 但在scrapy默认的情况下 两种中间件都在middlewares.py一个文件中
  • 爬虫中间件使用方法和下载中间件相同,且功能重复,通常使用下载中间件

Downloader Middlewares默认的方法:

  • process_request(self, request, spider):
    1. 当每个request通过下载中间件时,该方法被调用。
      2. 返回None值:没有return也是返回None,该request对象传递给下载器,或通过引擎传递给其他权重低的process_request方法
      3. 返回Response对象:不再请求,把response返回给引擎
      4. 返回Request对象:把request对象通过引擎交给调度器,此时将不通过其他权重低的process_request方法
  • process_response(self, request, response, spider):
    1. 当下载器完成http请求,传递响应给引擎的时候调用
      2. 返回Resposne:通过引擎交给爬虫处理或交给权重更低的其他下载中间件的process_response方法
      3. 返回Request对象:通过引擎交给调取器继续请求,此时将不通过其他权重低的process_request方法
  • 在settings.py中配置开启中间件,权重值越小越优先执行

fake_useragent

  • middlewares.py
  • from fake_useragent import UserAgent
  • class RandomUserAgentMiddleware(object):
  • def process_request(self, request, spider):
  • ua = UserAgent()
  • request.headers['User-Agent'] = ua.random
  • settings.py
  • DOWNLOADER_MIDDLEWARES = {
  • 'xxx项目名.middlewares.RandomUserAgentMiddleware': 300,
  • }

注意:UserAgent中间件设置为None,这样就不会启用,否则默认系统的这个中间会被启用。

ip_proxy

方法一【免费代理】:
  • settings.py
  • DOWNLOADER_MIDDLEWARES = {
  • '项目名.middlewares.RandomProxy': 543,
  • }
  • PROXY_LIST =[
  • {"ip_port": "123.207.53.84:16816", "user_passwd": "morganna_mode_g:ggc22qxp"},
  • # {"ip_port": "122.234.206.43:9000"},
  • ]
  • middlewares.py
  • from 项目名.settings import PROXY_LIST
  • class RandomProxy(object):
  • def process_request(self, request, spider):
  • proxy = random.choice(PROXY_LIST)
  • print(proxy)
  • if 'user_passwd' in proxy:
  • # 对账号密码进行编码,python3中base64编码的数据必须是bytes类型,所以需要encode
  • b64_up = base64.b64encode(proxy['user_passwd'].encode())
  • # 设置认证
  • request.headers['Proxy-Authorization'] = 'Basic ' + b64_up.decode()
  • # 设置代理
  • request.meta['proxy'] = proxy['ip_port']
  • else:
  • # 设置代理
  • request.meta['proxy'] = proxy['ip_port']
方法二【收费代理】:

收费代理ip:

  • # 人民币玩家的代码(使用abuyun提供的代理ip)
  • import base64
  • # 代理隧道验证信息 这个是在那个网站上申请的
  • proxyServer = 'http://proxy.abuyun.com:9010' # 收费的代理ip服务器地址,这里是abuyun
  • proxyUser = 用户名
  • proxyPass = 密码
  • proxyAuth = "Basic " + base64.b64encode(proxyUser + ":" + proxyPass)
  • class ProxyMiddleware(object):
  • def process_request(self, request, spider):
  • # 设置代理
  • request.meta["proxy"] = proxyServer
  • # 设置认证
  • request.headers["Proxy-Authorization"] = proxyAuth
  • 检测代理ip是否可用

在使用了代理ip的情况下可以在下载中间件的process_response()方法中处理代理ip的使用情况,如果该代理ip不能使用可以替换其他代理ip

  • class ProxyMiddleware(object):
  • ......
  • def process_response(self, request, response, spider):
  • if response.status != '200':
  • request.dont_filter = True # 重新发送的请求对象能够再次进入队列
  • return requst
  • settings.py
  • DOWNLOADER_MIDDLEWARES = {
  • 'myspider.middlewares.ProxyMiddleware': 543,
  • }

Scrapy-redis

  • settings.py 文件配置[注意:scrapy-redis 与 bloomfilter 二者只能选其一使用]
  • # scrapy-redis分布式配置 pip install scrapy-redis
  • # 重新指定调度器: 启用Redis调度存储请求队列!!!
  • SCHEDULER = "scrapy_redis.scheduler.Scheduler"
  • # 重新指定去重机制: 确保所有的爬虫通过Redis去重!!!
  • DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
  • # redis配置
  • REDIS_HOST = '127.0.0.1' # ip
  • REDIS_PORT = 6379 # 端口
  • # REDIS_PARAMS = {
  • # 'password': '', # 密码
  • # }

Scrapy-bloomfilter布隆过滤器

  • settings.py文件配置
  • # 布隆过滤器配置 pip install scrapy-redis-bloomfilter
  • # 重新指定调度器
  • SCHEDULER = "scrapy_redis_bloomfilter.scheduler.Scheduler"
  • # 重新指定去重机制
  • DUPEFILTER_CLASS = "scrapy_redis_bloomfilter.dupefilter.RFPDupeFilter"
  • # 使用哈希函数的数量,默认为6
  • BLOOMFILTER_HASH_NUMBER = 6
  • # 使用Bloomfilter的Redis内存,30表示2^30 = 128MB,默认为30
  • BLOOMFILTER_BIT = 30

设置 settings.py

常用变量

  • 设置数据导出编码(主要针对于json文件)
  • FEED_EXPORT_ENCODING = 'utf-8'
  • 设置User-Agent
  • USER_AGENT = ''
  • 设置最大并发数(默认为16)
  • CONCURRENT_REQUESTS = 32
  • 下载延迟时间(每隔多长时间请求一个网页)
  • DOWNLOAD_DELAY = 0.5
  • 请求头
  • DEFAULT_REQUEST_HEADERS = {'Cookie' : 'xxx'}
  • 添加项目管道
  • ITEM_PIPELINES = {'目录名.pipelines.类名' : 优先级}
  • COOKIES_ENABLED = False

启动运行

方法一【运行单个爬虫文件】:

  • 在根目录下创建start.py
    • from scrapy import cmdline
    • cmdline.execute("scrapy crawl hot".split())
    • # cmdline.execute("scrapy crawl hot -o hot.csv".split()) # 导出csv
    • # cmdline.execute("scrapy crawl hot -o hot.json".split()) # 导出json
    • # 注意: settings.py中设置导出编码 - 主要针对json文件
    • FEED_EXPORT_ENCODING = 'utf-8'

Excel打开csv时出现乱码

  • 解决办法:用记事本打开CSV,文件菜单中另存为UTF-8保存

方法二【 串行 运行完第一个才会运行下一个】:

  • 在根目录下创建main.py
    • from scrapy.cmdline import execute
    • execute("scrapy crawl 爬虫文件名1".split())
    • execute("scrapy crawl 爬虫文件名2".split())

方法三【在进程里面跑多个爬虫】:

  • 在根目录下创建main.py
    • from scrapy.crawler import CrawlerProcess
    • from scrapy.utils.project import get_project_settings
    • settings = get_project_settings()
    • crawler = CrawlerProcess(settings)
    • crawler.crawl('爬虫文件名1')
    • crawler.crawl('爬虫文件名2')
    • crawler.start()
    • crawler.start()
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门