python爬虫
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
python爬虫
基础爬虫
requests
import requests
# get请求
response = requests.get(url=url, params=params,headers=headers)
# post请求
response = requests.post(url=url, data=data_form, headers=headers)
get和post请求的数据格式都是字典除此之外比较重要的就是找到关键url
response返回数据
- text字符串
- content二进制
- json对象
持久化存储
文件夹操作
import os
if not os.path.exists('./test'):
os.mkdir('./test')
如果
普通存储
with open("../file/baidu.html", 'w', encoding='utf-8') as fp:
fp.write(response.text)
json格式json对象存储
dic_obj = response.json()
fp = open('./1.json','w',encoding='utf-8')
json.dump(dic_obj,fp=fp,ensure_ascii=False)
数据解析
- 标签定位
- 对标签或标签对应的属性中存储的数据值进行提取
正则re
-
re.findall(pattern,string,flag)
import re a = re.findall(ex,str,re.S)
ex为正则表达式str为给定的字符串re.S表明re会将这个字符串作为一个整体
-
re.sub(pattern,repl,string)
pattern:正则表达式
repl:要替换的字符串
string待处理字符串
# 将string中处于filter的字符串全部剔除 filter=[' ','\n'] re.sub('|'.join(filter),'',string)
特殊re字符
字符 | 含义 |
---|---|
. | 匹配除了换行符以外的任意字符 |
^ | 匹配以某字符串开头的字符串放在字符串前 |
$ | 匹配以某字符结尾的字符串放在字符串以后 |
* | 匹配前面的子模式零次或多次greedy。要匹配 * 字符请使用 \*。 |
+ | 匹配前面的子模式1次或多次greedy |
? | 匹配前面的子模式零次或1次greedy |
*?,+?,?? | 非greedy版本的*、+、? |
{m,n} | 匹配前面一个模式串m到n次greedy |
{m,n}? | 匹配前面一个模式串m到n次non-greedy |
\\ | 转义特殊字符或发出特殊序列信号 |
[] | 匹配字符集合 |
| | orA|B,匹配模式串A或模式串B |
() | 匹配括号以内的部分在括号内包含模式串 |
特殊序列
序列 | 含义 |
---|---|
\b | 匹配空字符串但仅在单词的开头或结尾。 |
\B | 匹配空字符串但不在单词的开头或结尾。 |
\d | 匹配任意十进制数字等价于[0-9] |
\D | 匹配任意非十进制数字等价于[^\d] |
\s | 匹配任意空白字符 |
\S | 匹配任意非空白字符 |
\w | 匹配任意字母数字字符 |
\W | 匹配任意非字母或数字字符 |
\\ | 匹配反斜杠 |
flags
flags | 含义 |
---|---|
A | 使\w \w \b \b \d \d匹配相应的ASCII字符类别 |
I | 执行不区分大小写的匹配。 |
M | "^“匹配行首(换行符之后)以及字符串。”$"匹配行尾(换行符之前)以及字符串的结尾。 |
S | "."匹配任何字符不再排除换行符。 |
X | 忽略空格和注释以获得更好看的正则。 |
使用的时候为函数的flag参数指定re.S
即可
xpath
最常用且最高效便捷的一种解析方式通用性最强。
- 实例化一个etree对象且需要将被解析的页面源码数据加载到该对象中
- 调用etree对象中的xpath方法结合xpath表达式实现标签的定位和内容的捕获
实例化etree对象
from lxml import etree
tree = etree.parse(filePath)#加载html文档源码
tree.HTML('page_text')#将数据加载到该对象中
tree.xpath('xpath表达式')#解析
xpath表达式
语法 | 含义 | 示例 |
---|---|---|
/ | 从当前标签位置开始定位标签 | /html/div |
// | 从任意位置定位标签 | //div |
[@] | 属性定位 | //div[@class=“song”]定位到class为song的div |
[] | 索引定位 | //div/p[3]定位第三个p标签(索引从1开始) |
/text() | 获取标签的直系文本 | //div/p[3]/text(),定位第三个p标签并取出其直系文本 |
//text() | 获取标签的所有文本 | //div//text(),取出所有div标签中的所有文本内容 |
/@attrName | 获取属性值 | //img/@src取出所有img标签的src属性值 |
./ | 当前标签 |
反爬绕过
伪装绕过
-
UAUser-Agent伪装
安装
fake_useragent
包pip install fake_useragent
然后
from fake_useragent import UserAgent ua = UserAgent() user_agent = ua.random
-
cookie伪装
-
Referer伪装
验证码绕过
- 第三方验证码识别api云打码识别收费较低
模拟登陆
在使用requests进行模拟登陆的时候可能需要使用requests.Session才可以
session = requests.Session()
response = session.get(url=url, headers=headers)
在登录过后想要获取其他页面的数据需要携带cookie该cookie是登陆后返回的set-cookie的值如果使用session则session会自动存储cookie下次请求就没有必要携带cookie
控制请求间隔
对于只有一个线程或一个进程的程序使用time.sleep
来控制间隔
代理
绕过ip封禁在requests中指定proxies参数值,这是一个字典
代理池的使用暂时遇到两种格式
协程aiohttp.ClientSession.get
、scrapy格式
proxies = ["http://61.216.185.88:60808", "http://121.13.252.58:41564","http://113.124.86.24:9999","http://27.42.168.46:55481","http://117.114.149.66:55443"]
requests格式
proxies = [{"http": "61.216.185.88:60808"}, {"http": "121.13.252.58:41564"},{"http": "113.124.86.24:9999"},{"http": "27.42.168.46:55481"},{"http": "117.114.149.66:55443"}]
快代理代理爬取函数
def get_proxy():
ip_list = []
port_list = []
anonymity_level_list = []
type_list = []
location_list = []
response_speed_list = []
page_nums = 15
for page_num in range(1, page_nums):
url = "https://www.kuaidaili.com/free/inha/" + str(page_num) + '/'
headers = {
'User-Agent': user_agents_pool[page_num % len(user_agents_pool)]
}
# response = requests.get(url=url, headers=headers, proxies=proxies[page_num % 5])
response = requests.get(url=url, headers=headers)
print(response.status_code)
page_text = etree.HTML(response.text)
ip_s = page_text.xpath("//td[@data-title='IP']/text()")
port_s = page_text.xpath("//td[@data-title='PORT']/text()")
anonymity_level_s = page_text.xpath("//td[@data-title='匿名度']/text()")
type_s = page_text.xpath("//td[@data-title='类型']/text()")
location_s = page_text.xpath("//td[@data-title='位置']/text()")
response_speed_s = page_text.xpath("//td[@data-title='响应速度']/text()")
ip_list.extend(ip_s)
port_list.extend(port_s)
anonymity_level_list.extend(anonymity_level_s)
type_list.extend(type_s)
location_list.extend(location_s)
response_speed_list.extend(response_speed_s)
time.sleep(2)
dic = {
'ip': ip_list,
'port': port_list,
'anonymity level': anonymity_level_list,
'type': type_list,
'location': location_list,
'response_speed': response_speed_list
}
df = pd.DataFrame(dic)
# 去除ip列和port列都重复的行
df.drop_duplicates(df[df.duplicated('ip')&df.duplicated('port')], inplace=True)
df.to_csv(path_or_buf='./proxy.csv', encoding='gbk', index=False)
检测到非法调试
卡死在debbuge处
-
让开发者工具界面作为独立窗口存在
-
hook代码注入绕过注意刷新页面后就需要重新注入。在
源代码
添加新片段
新片段
为以下代码之后继续执行即可var AAA=Function.prototype.constructor Function.prototype.constructor=function(x){ if(X!="debugger"){ return AAA(x) }; return function(){}; }
异步爬虫
在爬虫中使用异步实现高性能的数据爬取操作
异步爬虫方式
-
多进程或多线程阻塞操作可以异步执行但进程和线程不可无限制创建
-
线程池或进程池可以降低系统对进程或线程创建和销毁的一个频率从而很好地降低系统的开销但池中的数量仍有上限
import time from multiprocessing.dummy import Pool # 进程池和线程池 def get_page(): return url_list=[] start_time = time.time() #实例化一个拥有4个线程的线程池 pool = Pool(4) # 将列表中每一个列表元素传递给函数进行处理并返回一个列表 result_list = pool.map(get_page, url_list) pool.close() pool.join()#主线程等待子线程运行结束后再结束运行
线程池处理的是阻塞且耗时的操作
-
单线程+异步协程
-
event_loop
事件循环将函数注册到该事件循环当满足某些条件函数就会被执行 -
coroutine
协程对象 -
task
任务是协程对象的进一步封装包含了任务的各个状态# 创建task的第一种方式 task = asyncio.create_task(...) #创建task的第二种方式 task = asyncio.ensure_future(...)
-
future
代表将来执行或还没执行的任务实际上和task没有本质区别 -
async
定义一个协程 -
await
用来将可等待的对象挂起(协程对象、Future、Task)等待对象的值得到结果后再继续向下走
对于主线程是
loop=get_event_loop()
. 对于其他线程需要首先loop=new_event_loop()
,然后set_event_loop(loop)
。
new_event_loop()
是创建一个event loop
对象而set_event_loop(eventloop对象)
是将event loop对象指定为当前协程的event loop一个协程内只允许运行一个event loop不要一个协程有两个event loop交替运行。 -
协程不是由计算机提供而是人为创造的是一种用户态内的上下文切换技术。简而言之就是通过一个线程实现代码块互相切换。
在一个线程中如果遇到IO等待时间线程不会一直等着而是利用空闲的时间去干别的事。(IO多路复用)
实现协程
- greenlet早期模块
- yield关键字
- asyncio装饰器
- async、await关键字
多任务异步协程asyncio
import asyncio
import time
async def request(url):
print(url)
# 在异步协程中如果出现了同步模块相关的代码那么就无法实现异步
# 使用time.sleep就无法实现异步
# 对阻塞操作手动挂起
await asyncio.sleep(2)
urls = {
"www.baidu.com",
"www.163.com",
"www.souhu.com"
}
# 任务列表存放多个任务对象
tasks = []
# 生成事件循环
loop = asyncio.new_event_loop()
for url in urls:
c = request(url=url)
task = asyncio.ensure_future(c, loop=loop)
tasks.append(task)
# 使用wait封装task列表然后一次性往事件循环中注册任务列表中的任务
results = loop.run_until_complete(asyncio.wait(tasks))
在使用requests进行爬取时requests.get基于同步模块必须使用基于异步请求的模块aiohttp
async def get_page(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
# text返回文本read返回二进制json返回json对象
page_text = await response.text()# 使用await进行挂起
with open("../file/"+url[7:]+".html", 'w', encoding='utf-8') as fp:
fp.write(page_text)
uvloop
是asyncio的事件循环的替代方案事件循环>默认asyncio的事件循环
pip install uvloop
import uvloop
asyncio.set_event_loop_policy(uvloop.EvventLoopPolicy())
其他的处理与asyncio的事件循环处理的方式一致
爬虫框架
selenium
- 便捷地获取网站中动态加载的数据
- 便捷实现模拟登陆
基于浏览器的自动化模块
from selenium import webdriver
import selenium.webdriver.Chrome.options as op
from selenium.webdriver import ActionChains
from lxml import etree
from time import sleep
# Chrome_options = webdriver.ChromeOptions()
# 设置无头模式浏览器无可视化界面运行
# Chrome_options.headless = True
# 将驱动程序放在python目录下可以不用指定executable_path路径
# browser = webdriver.Chrome(options=Chrome_options)
browser = webdriver.Chrome()
browser.get(url = "https://cn.bing.com/")
page_text = browser.page_source
# print(page_text)
# 搜索框定位
search_input = browser.find_element(value="sb_form_q")
search_input.send_keys("你好")
# 提交按钮定位
go_input = browser.find_element(value="sb_form_go")
go_input.submit()
# 执行js代码将页面滚动到最底部
browser.execute_script(script="window.scrollTo(0, document.body.scrollHeight)")
# 回退
browser.back()
#前进
browser.forward()
sleep(10)
# 关闭浏览器
browser.quit()
标签定位
search_input = browser.find_element(value="sb_form_q")
默认是通过id定位标签
iframe
browser.switch_to.frame("待切换的iframe的id")# 切换标签定位作用域
查看一个页面的第一层iframe有多少在控制台执行
frames.length()
通过索引可以获取对应的iframe
frames[i]
查看第i个iframe的子iframe个数
frames[i].frames.length()
动作链
from selenium.webdriver import ActionChains
action = ActionChain(browser)
action.click_and_hold(标签变量)#点击并抓住这个标签
action.move_by__offset(x,y).perfome()#拖动到某个位置并立即执行
action.release()#释放动作链
规避检测
# 规避检测
options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 禁用浏览器自动控制
options.add_argument("--disable-blink-features=AutomationControlled")
无头浏览器
# 无头浏览器
options.add_argument("--headless")
options.add_argument("--disable-gpu")
scrapy
集成了很多功能并且具有很强通用性的项目模板其功能为
- 高性能的持久化存储
- 异步数据下载
- 高性能的数据解析
- 分布式
scrapy原理
scrapy五大核心组件
-
调度器过滤器和队列过滤完成的请求放入队列
-
管道
-
引擎
-
下载器
-
spider发送请求数据解析
中间件
-
下载中间件处于引擎与下载器之间批量拦截到整个工程中所有的请求和响应
-
拦截请求
可以做UA伪装设置代理ip
-
拦截响应
可以篡改响应数据响应对象
-
-
爬虫中间件处于spider与引擎之间
创建工程
在终端输入scrapy startproject project_name
目录结构
└─scrapy_test
│ items.py
│ middlewares.py
│ pipelines.py
│ settings.py
│ __init__.py
│
└─spiders
__init__.py
-
spiders
爬虫文件夹在该文件夹下必须创建一个爬虫文件scrapy genspider spiderName www.xxx.com
如果是基于CrawlSpider则按以下方式创建爬虫文件scrapy genaspider -t crawl xxx www.xxx.com
-
settings.py
存放工程配置 -
items.py
数据的封装使用scrapy.field
-
pipelines.py
专门用来处理item
工程执行
scrapy crawl spliderName
就会执行指定的爬虫文件
--nolog
:不打印日志
如果需要在某个python文件中对spider进行调用那么可以采用以下方式
-
cmd方式cmdline.execute或者os.system该方式需要切换工作目录比较麻烦
-
CrawlerProcess方式相比cmd来说更方便而且可以看日志
from crawler.crawler.spiders.crawlAll import CrawlallSpider from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings if __name__ == '__main__': process = CrawlerProcess(get_project_settings()) process.crawl(CrawlallSpider) process.start()
CrawlallSpider
是自定义的爬虫类其文件路径为/crawler/crawler/spider/crawlAll
-
CrawlerRunner方式看不到日志
from scrapy.crawler import CrawlerRunner from scrapy.utils.project import get_project_settings from twisted.internet import reactor from crawler.crawler.spiders.crawlAll import CrawlallSpider runner = CrawlerRunner(get_project_settings()) # [runner.crawl(spider) for spider in spiders] runner.crawl(CrawlallSpider) d = runner.join() d.addBoth(lambda _: reactor.stop()) reactor.run()
注意在使用第二种或第三种方式时在setting文件中定义的管道等配置可能不会被读取最好放在爬虫文件的custom_settings中
spiders
指的是使用genspider
创建的处于spiders
目录下的文件
-
name
爬虫源文件名 -
allow_domains
限定哪些域名下的url可以别允许发送请求但一般不用 -
start_urls
在该列表中存放的url会被自动发送请求 -
parse
函数用于数据解析response.xpath
与etree的xpath有一些不同spider经过xpath后的结果是一个Selector
列表通过extract
函数可以取出每个selector
的data
参数存放的值
-
start_requests
函数最开始发起请求的地方
setting配置文件
# 是否遵循robots.txt
ROBOTSTXT_OBEY = False
# 只显示error等级的日志信息
LOG_LEVEL = 'ERROR'
# 指定代理
USER_AGENT = ''
# 开启管道
ITEM_PIPELINES = {
'xxxxx.pipelines.xxxxxPipeline':300,#xxxxx代指工程名300表示优先级数值越小优先级越高
}
# 开启爬虫中间件
SPIDER_MIDDLEWARES = {
'xxxxx.middlewares.xxxxxSpiderMiddleware': 543,
}
# 开启下载中间件
DOWNLOADER_MIDDLEWARES = { 'xxxxx.middlewares.xxxxxDownloaderMiddleware': 543,
}
# 在高延迟情况下设置的最大下载延迟
AUTOTHROTTLE_MAX_DELAY = 60
# 配置Scrapy的最大并发请求 (default: 16)
CONCURRENT_REQUESTS = 32
# 指定一网站的请求延迟
DOWNLOAD_DELAY = 3
# 禁用 cookies (enabled by default)
COOKIES_ENABLED = False
# 指定基于ImagesPipeline管道的存储目录路径
IMAGES_STORE = 'path'
在执行工程时通过指定-s
参数可以设置临时配置比如
scrapy crawl spiderName -s DOWNLOAD_DELAY=10
在自定义的spider类中添加一个变量如下
class ImgSpiderSpider(scrapy.Spider):
name = 'img_spider'
allowed_domains = ['sc.chinaz.com']
start_urls = ['https://sc.chinaz.com/tupian/xingkongtupian.html']
custom_settings = {
'DOWNLOAD_DELAY': 5
}
def parse(self, response):
print(self.crawler.settings.get('DOWNLOAD_DELAY'))
然后执行工程可以得到DOWNLOAD_DELAY
的值8即为custom_settings中定义的值。
持久化存储
基于终端
将parse
方法的返回值存储于本地文件中
scrapy crawl spider_name -o filepath
存储的文件格式有限制
基于管道
-
在爬虫文件的
parse
中进行数据解析 -
在
items.py
中定义相关的属性 -
将解析的数据封装存储到item类型的对象里使用
scrapy.field
进行对象的定义obj1 = scrapy.field() obj2 = scrapy.field()
-
从
items.py
importitems
类并进行实例化然后进行将解析的数据存入item对象中最后使用yield
把item
传递给管道from items import xxxxItem def parse(self, response): ..... ..... ..... item = xxxxItem()#实例化 item[obj_name] = data yield item
-
在
pipelines.py
的process_item
方法中接受爬虫文件提交过来的item对象存储的数据并进行持久化存储操作注意需要重写父类方法# 对父类的方法进行重写该方法只在开始爬虫的时候被调用一次 fp = None def open_spider(self,spider): self.fp = open(...) def close_spider(self, spider): self.fp.close()
-
在配置文件中开启管道
数据库持久化存储
可以自己在pipelines.py
中新写一个管道类然后在其中重写方法
class sqlitePipeLine(object)
conn = None
cursor = None
def open_spider(self, spider):
self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='123456',charset='utf8')
def process_item(self,spider):
self.cursor = self.conn.cursor()
try:
self.cursor.execute('sql语句')
self.conn.commit()
except Exception as e:
print(e)
self.conn.rollback()
return item
def close_spider(self, spider)
self.cursor.close()
self.conn.close()
最后别忘记还要在setting
文件中添加该管道。另外只有在process_item
方法中return item
其他pipelie才可以拿到item并对其中的数据做处理
请求传参
使用场景深度爬取需要发出新的请求
yield scrapy.Request(url=new_url, callback=self.xxx_parse)
自定义解析函数后可以将其作为回调函数
def xxx_parse(self, response):
.....
请求传参指的是将当前parse中的变量传递给其他parse函数可以在Request中指定meta参数
yield scrapy.Request(url=new_url, callback=self.xxx_parse, meta={'item':item})
然后在xxx_parse
解析函数里面按照字典的方式取数据
item = response.meta['item']
另外Request既可用于get请求又可用于post请求指定method
参数进行post请求发送还可以通过FormReques
ImagesPipeline图片数据爬取
专门用于图片数据爬取只需要将图片的url发送给该管道管道就可以对图片的src进行请求发送获取图片的二进制数据。
继承ImagesPipeline后重写以下方法
from scrapy.pipelines.images import
class MyInagesPipeline(ImagesPipeline):
# 提供url自动下载图片
def get_media_requests(self, item, info):
yield scrapy.Request(item['url'])
# 指定图片存储名
def file_path(self, request, response=None, info=None, *, item=None):
img_name = request.url.split('/')[-1]
return img_name
#将item传递给下一个即将执行的管道类
def item_completed(self, results, item, info):
return item
图片存储的目录需要通过setting来指定具体查看setting配置文件。
middlewares中间件
中间件的使用需要在setting文件中开启
下载中间件DownloadMiddlewares
class xxxDownloadMiddlewares(object):
user_agent_pool=[xxxxxxx]
proxy_http = [xxxxx]
proxy_https = [xxxxx]
# 拦截正常请求
def process_request(self, request, spider):
# UA伪装
request.headers['User-Agent'] = random.choice(self.user_agent_pool)
return None
# 拦截所有的响应
def process_response(self, request, response, spider):
return response
# 拦截发生异常的请求
def process exception(self, request, exception, spider):
if request.url.split(':')[0]=='http':
# 代理ip 'proxy'是request固定携带的
request.meta[ 'proxy'] = random.choice(proxy_http)
else
request.meta[ 'proxy'] = random.choice(proxy_https)
return request
CrawSpider全站数据爬取
spider的子类。
全站数据爬取的方式
- 基于spider手动请求
- 基于CrawSpider
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class AllspiderSpider(CrawlSpider):
name = 'allSpider'
allowed_domains = ['www.xxx.com']
start_urls = ['http://www.xxx.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
item = {}
#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
-
链接提取器LinkExtractor根据指定规则进行指定链接的提取即便有重复的链接也会自动过滤
- allow一个用来提取链接的正则表达式
-
规则解析器Rule
link
指定链接提取器callback
:指定回调函数follow
指定是否可以自动更换start_url其实就是将链接提取器继续作用到链接提取器提取到的链接所对应的页面中。
-
parse_item
对链接提取器中的链接得到的response进行解析
链接提取器
from scrapy.linkextractors import LinkExtractor
协程引用
在setting配置文件中添加
TWISTED_REACTOR = 'twisted.internet.asyncioreactor.AsyncioSelectorReactor'
日志
[Scrapy的log日志功能 - 言守中 - 博客园 (cnblogs.com)](https://www.cnblogs.com/shouzhong/p/7685822.html#:~:text=LOG_LEVEL 默认%3A ‘DEBUG’log的最低级别 LOG_STDOUT 默认%3A False 如果为,True进程所有的标准输出 (及错误)将会被重定向到log中。 例如执行 print “hello” 其将会在Scrapy log中显示)
分布式爬虫
搭建分布式机群让其对一组资源进行分部联合爬取可以提高爬取的效率
实现分布式需要安装scrapy-redis
原生的scrapy不能实现分布式爬虫
-
创建工程
-
创建基于
CrawlSpider
的爬虫 -
修改爬虫文件
from scrapy_redis.spiders import RedisCrawlSpider class LocalSpider(RedisCrawlSpider): name = '58' # 注释以下两行变量 # allowed_domains = ['cq.58.com'] # start_urls = ['http://cq.58.com/'] redis_key = '58' # 被共享的调度器队列的名称 rules = ( Rule(LinkExtractor(allow=r'shouji/pn\d+'), callback='parse_item', follow=True), ) def parse_item(self, response): item = {} title_list = response.xpath("//td[@class='t'/a[1]//text()").extract() location_list = response.xpath("//td[@class='t'/p[1]//text()").extract() price_list = response.xpath("//td[@class='t'/p[3]//text()").extract() print(title_list) for key, title in enumerate(title_list): item = ScrapyRedisTestItem() item['title'] = title item['location'] = location_list[key] item['price'] = price_list[key] yield item
-
修改
setting
配置文件-
指定可以被共享的管道
ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline':400 }
-
指定调度器
# 增加一个去重容器类的配置使用redis的set集合来存储请求的指纹数据从而实现请求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" #使用scrapy-redis自己调度器,不使用scrapy默认的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" #配置调度器是否要持久化不调度状态持久化不清理redis缓存允许暂停/启动爬虫 SCHEDULER_PERSIST = True
-
-
redis相关配置
redis的配置文件
.conf
文件# 关闭保护模式允许写数据 protected-mode no # 注释以下语句 # bind 127.0.0.1
-
启动redis服务
redis-server 配置文件
-
启动客户端其他要使用redis的分布式主机
redis-cli
-
执行工程
scrapy runspider xxx.py
-
启动客户端后指定调度器队列名并向其中放入起始url
lpush 调度器队列名 起始url
-
在setting文件中指定提供redis服务的主机和端口
REDIS_HOST = '127.0.0.1' REDIS_PORT = 6379
-
增量式爬虫
用于检测网站数据更新的情况只会爬取网站最新更新出来的数据这意味着需要对上一次爬取得到的链接进行存储可以存到redis的set集合中
scrapy-redis在进行爬取后会自动去重也就是说遇到重复的链接将不再存储
报错修改
编码为utf-8 response仍为乱码
一般来说response返回的内容如果为乱码那么基本就是没有设置正确的编码这可以使用以下代码解决
response.text.encode('utf8')
但是还有一个情况当在header中设置了以下参数也会出现乱码
"Accept-Encoding":"gzip, deflate, br"
accept-encoding表示你发送请求时告诉服务器我可以解压这些格式的数据因此爬取的response被压缩成了这样的格式。requests不支持br所以一旦Accept-Encoding包含br且服务器返回br格式的数据那么就会出现乱码故删除br即可解决问题。
"Accept-Encoding":"gzip, deflate"
pip安装包时报错 ERROR: Failed building wheel for xxx
去以下网站下载对应的whl包并使用pip安装
Archived: Python Extension Packages for Windows - Christoph Gohlke (uci.edu)
Scrapy 中 ImagesPipeline 无法执行
pip install pillow
启用下载中间件后卡死在telnet的监听处
检查process_request函数返回值是不是None