scrapy框架的运行流程以及数据传递过程:
引擎(engine):负责数据和信号在不腰痛模块间的传递
调度器(scheduler):实现一个队列,存放引擎发过来的request请求对象
下载器(downloader):发送引擎发过来的request请求,获取响应,并将响应交给引擎
爬虫(spider):处理引擎发过来的response,提取数据,提取url,并交给引擎
管道(pipeline):处理引擎传递过来的数据,比如存储
下载中间件(downloader middleware):可以自定义的下载扩展,比如设置代理ip
爬虫中间件(spider middleware):可以自定义request请求和进行response过滤,与下载中间件作用重复
- 解析并获取scrapy爬虫中的数据:
- 1. response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
- 2. extract() 返回一个包含有字符串的列表,配合[0]使用
- 3. extract_first() 返回列表中的第一个字符串,列表为空没有返回None
- 4. get() 提取列表中第1个文本内容(等同于extract_first())
-
- 我们知道可以通过scrapy.Request()指定method、body参数来发送post请求;但是通常使用scrapy.FormRequest()来发送post请求
-
注意:scrapy.FormRequest()能够发送表单和ajax请求,参考阅读 https://www.cdsy.xyz/computer/programme/Python/230430/cd43347.html
response.url:当前响应的url地址
- 1. response.request.url:当前响应对应的请求的url地址
- 2. response.headers:响应头
- 3. response.requests.headers:当前响应的请求头
- 4. response.body:响应体,也就是html代码,byte类型
- 5. response.status:响应状态码
-
- 1.在settings.py中通过设置COOKIES_DEBUG=TRUE 能够在终端看到cookie的传递传递过程
- 2.如果start_url地址中的url是需要登录后才能访问的url地址,则需要重写3.start_request方法并在其中手动添加上cookie
- 4.如果重新start_requests()方法,则需要去掉start_urls变量
-
在爬虫文件的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的下载中间件
管道的基本使用:
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
- 在settings.py设置开启pipeline
-
- ITEM_PIPELINES = {
- 'myspider.pipelines.ItcastFilePipeline': 400, # 400表示权重
- 'myspider.pipelines.ItcastMongoPipeline': 500, # 权重值越小,越优先执行!
- }
-
-
- 1. 不同的pipeline可以处理不同爬虫的数据,通过spider.name属性来区分
- 2. 不同的pipeline能够对一个或多个爬虫进行不同的数据处理的操作,比如一个进行数据清洗,一个进行数据的保存
- 3. 同一个管道类也可以处理不同爬虫的数据,通过spider.name属性来区分
-
- # 【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))
-
- 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()
-
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()
-
- 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()
-
- # 管道
- ITEM_PIPELINES = {
- 'LianJiaSpider.pipelines.LianjiaspiderPipeline': 300,
- }
-
- 1.下载器中间件
- 2.爬虫中间件
-
- 1. 对header以及cookie进行更换和处理
- 2. 使用代理ip等
- 3. 对请求进行定制化操作,
-
- 但在scrapy默认的情况下 两种中间件都在middlewares.py一个文件中
-
- 爬虫中间件使用方法和下载中间件相同,且功能重复,通常使用下载中间件
-
- from fake_useragent import UserAgent
- class RandomUserAgentMiddleware(object):
- def process_request(self, request, spider):
- ua = UserAgent()
- request.headers['User-Agent'] = ua.random
-
- DOWNLOADER_MIDDLEWARES = {
- 'xxx项目名.middlewares.RandomUserAgentMiddleware': 300,
- }
-
注意:UserAgent中间件设置为None,这样就不会启用,否则默认系统的这个中间会被启用。
- 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"},
- ]
-
- 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的情况下可以在下载中间件的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
-
- DOWNLOADER_MIDDLEWARES = {
- 'myspider.middlewares.ProxyMiddleware': 543,
- }
-
- # 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': '', # 密码
- # }
-
- # 布隆过滤器配置 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
-
- 设置数据导出编码(主要针对于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
-
- 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保存
- from scrapy.cmdline import execute
-
- execute("scrapy crawl 爬虫文件名1".split())
- execute("scrapy crawl 爬虫文件名2".split())
-
- 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()
-