爬虫——小结
爬虫原理与数据抓取
requests简单使用
添加 headers 和 查询参数
如果想添加 headers,可以传入headers参数来增加请求头中的headers信息。如果要将参数放在url中传递,可以利用 params 参数
import requests kw = {'wd':'长城'} headers = {"user-agent": "mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/54.0.2840.99 safari/537.36"} # params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode() response = requests.get("http://www.baidu.com/s?", params = kw, headers = headers) # 查看响应内容,response.text 返回的是unicode格式的数据 print (response.text) # 查看响应内容,response.content返回的字节流数据 print (respones.content) # 查看完整url地址 print (response.url) # 查看响应头部字符编码 print (response.encoding) # 查看响应码 print (response.status_code)
使用response.text 时,requests 会基于 http 响应的文本编码自动解码响应内容,大多数 unicode 字符集都能被无缝地解码。 使用response.content 时,返回的是服务器响应数据的原始二进制字节流,可以用来保存图片等二进制文件。
requests默认自带的accept-encoding导致或者新浪默认发送的就是压缩之后的网页
但是为什么content.read()没有问题,因为requests,自带解压压缩网页的功能
当收到一个响应时,requests 会猜测响应的编码方式,用于在你调用response.text 方法时对响应进行解码。requests 首先在 http 头部检测是否存在指定的编码方式,如果不存在,则会使用 chardet.detect来尝试猜测编码方式(存在误差)
更推荐使用response.content.deocde()
requests深入
# 如果是json文件可以直接显示 print (response.json()) # unquote将url格式的中文还原 a = requests.utils.unquote('http://www.baidu.com/f?kw=%e6%9d%e6%85%') print(a) http://www.baidu.com/f?kw=李子 通过本地环境变量 http_proxy 和 https_proxy 来配置代理: export http_proxy="http://12.34.56.79:9527" export https_proxy="https://12.34.56.79:9527"
私密代理验证(特定格式) 和 web客户端验证(auth 参数)
私密代理
import requests # 如果代理需要使用http basic auth,可以使用下面这种格式: proxy = { "http": "mr_mao_hacker:sffqry9r@61.158.163.130:16816" } response = requests.get("http://www.baidu.com", proxies = proxy) print (response.text)
web客户端验证
如果是web客户端验证,需要添加 auth = (账户名, 密码)
1 import requests 2 3 auth=('test', '123456') 4 5 response = requests.get('http://192.168.199.107', auth = auth) 6 7 print (response.text)
cookies
1 import requests 2 3 response = requests.get("http://www.baidu.com/") 4 5 # 7\. 返回cookiejar对象: 6 cookiejar = response.cookies 7 8 # 8\. 将cookiejar转为字典: 9 cookiedict = requests.utils.dict_from_cookiejar(cookiejar) 10 11 print (cookiejar) 12 13 print (cookiedict)
session
实现人人网登录
import requests # 1\. 创建session对象,可以保存cookie值 ssion = requests.session() # 2\. 处理 headers headers = {"user-agent": "mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/54.0.2840.99 safari/537.36"} # 3\. 需要登录的用户名和密码 data = {"email":"", "password":""} # 4\. 发送附带用户名和密码的请求,并获取登录后的cookie值,保存在ssion里 ssion.post("http://www.renren.com/plogin.do", data = data) # 5\. ssion包含用户登录后的cookie值,可以直接访问那些登录后才可以访问的页面 response = ssion.get("http://www.renren.com/410043129/profile") # 6\. 打印响应内容 print (response.text)
处理https请求 ssl证书验证
requests也可以为https请求验证ssl证书:
- 要想检查某个主机的ssl证书,你可以使用 verify 参数(也可以不写)
- 如果ssl证书验证不通过,或者不信任服务器的安全证书,则会报出sslerror,据说 12306 证书是自己做的
- 如果我们想跳过 12306 的证书验证,把 verify 设置为 false 就可以正常请求了
sslerror: ("bad handshake: error([('ssl routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)
非结构化数据与结构化数据提取
xpath与lxml类库
- bookstore//book
选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置
- /bookstore/book[last()-1]
选取属于 bookstore 子元素的倒数第二个 book 元素
- /bookstore/book[position()<3]
选取最前面的两个属于 bookstore 元素的子元素的 book 元素
- /bookstore/book[price>35.00]/title
选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00
- //book/title | //book/price
选取 book 元素的所有 title 和 price 元素
- //title | //price
选取文档中的所有 title 和 price 元素
- /bookstore/book/title | //price
选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素
- //li//span
获取<li> 标签下的所有 <span> 标签
- //li/a//@class
获取 <li> 标签下的<a>标签里的所有 class
- //li[last()]/a/@href
获取最后一个 <li> 的 <a> 的 href
json
json.loads()
从json到python的类型转化
json.dumps()
从python原始类型向json类型的转化
chardet是一个非常优秀的编码识别模块,可通过pip安装。chardet.detect()返回字典, 其中confidence是检测精确度
json.dump()
将python内置类型序列化为json对象后写入文
1 import json 2 3 liststr = [{"city": "北京"}, {"name": "大刘"}] 4 json.dump(liststr, open("liststr.json","w"), ensure_ascii=false) 5 6 dictstr = {"city": "北京", "name": "大刘"} 7 json.dump(dictstr, open("dictstr.json","w"), ensure_ascii=false)
json.load()
读取文件中json形式的字符串元素 转化成python类型
1 import json 2 3 strlist = json.load(open("liststr.json")) 4 print strlist 5 6 # [{u'city': u'\u5317\u4eac'}, {u'name': u'\u5927\u5218'}] 7 8 strdict = json.load(open("dictstr.json")) 9 print strdict 10 # {u'city': u'\u5317\u4eac', u'name': u'\u5927\u5218'}
queue
- queue是python中的标准库,可以直接import queue引用;队列是线程间最常用的交换数据的形式
- 对于资源,加锁是个重要的环节。因为python原生的list,dict等,都是not thread safe的。而queue,是线程安全的,因此在满足使用条件下,建议使用队列
- 初始化: class queue.queue(maxsize) fifo 先进先出
- 包中的常用方法:
- queue.qsize() 返回队列的大小
- queue.empty() 如果队列为空,返回true,反之false
- queue.full() 如果队列满了,返回true,反之false
- queue.full 与 maxsize 大小对应
- queue.get([block[, timeout]])获取队列,timeout等待时间
- 创建一个“队列”对象
import queue myqueue = queue.queue(maxsize = 10)
- 将一个值放入队列中
myqueue.put(10)
- 将一个值从队列中取出
myqueue.get()
beautifulsoup4
lxml 只会局部遍历,而beautiful soup 是基于html dom的,会载入整个文档,解析整个dom树,因此时间和内存开销都会大很多,所以性能要低于lxml
1 print soup.name 2 # [document] #soup 对象本身比较特殊,它的 name 即为 [document] 3 4 print soup.head.name 5 # head #对于其他内部标签,输出的值便为标签本身的名称 6 7 print soup.p.attrs 8 # {'class': ['title'], 'name': 'dromouse'} 9 # 在这里,我们把 p 标签的所有属性打印输出了出来,得到的类型是一个字典。 10 11 print soup.p['class'] # soup.p.get('class') 12 # ['title'] #还可以利用get方法,传入属性的名称,二者是等价的 13 14 soup.p['class'] = "newclass" 15 print soup.p # 可以对这些属性和内容等等进行修改 16 # <p class="newclass" name="dromouse"><b>the dormouse's story</b></p> 17 18 del soup.p['class'] # 还可以对这个属性进行删除 19 print soup.p 20 # <p name="dromouse"><b>the dormouse's story</b></p> 21 22 print soup.p.string 23 # the dormouse's story 24 25 print type(soup.p.string) 26 # in [13]: <class 'bs4.element.navigablestring'> 27 28 # comment 对象是一个特殊类型的 navigablestring 对象,其输出的内容不包括注释符号 29 print soup.a 30 # <a class="sister" href="http://example.com/elsie" id="link1"><!-- elsie --></a> 31 32 print soup.a.string 33 # elsie 34 35 print type(soup.a.string) 36 # <class 'bs4.element.comment'> 37 # tag 的 .content 属性可以将tag的子节点以列表的方式输出 38 print soup.head.contents 39 #[<title>the dormouse's story</title>] 40 print soup.head.contents[0] 41 #<title>the dormouse's story</title> 42 43 # .children 返回的是一个 list 生成器对象,通过遍历获取所有子节点 44 print soup.head.children 45 #<listiterator object at 0x7f71457f5710> 46 47 for child in soup.body.children: 48 print child 49 50 # 所有子孙节点: .descendants 属性 51 .contents 和 .children 属性仅包含tag的直接子节点,.descendants 属性可以对所有tag的子孙节点进行递归循环,和 children类似,我们也需要遍历获取其中的内容 52 for child in soup.descendants: 53 print child
动态html处理和机器图像识别
selenium
1 # 调用键盘按键操作时需要引入的keys包 2 from selenium.webdriver.common.keys import keys 3 4 # 生成当前页面快照并保存 5 driver.save_screenshot("baidu.png") 6 7 # 打印网页渲染后的源代码 8 print driver.page_source 9 10 # 获取当前页面cookie 11 print driver.get_cookies() 12 13 # ctrl+a 全选输入框内容 14 driver.find_element_by_id("kw").send_keys(keys.control,'a') 15 16 # ctrl+x 剪切输入框内容 17 driver.find_element_by_id("kw").send_keys(keys.control,'x') 18 19 # 模拟enter回车键 20 driver.find_element_by_id("su").send_keys(keys.return) 21 22 # 获取当前url 23 print driver.current_url 24 25 # 关闭当前页面,如果只有一个页面,会关闭浏览器 26 # driver.close() 27 28 # 关闭浏览器 29 driver.quit()
页面操作
1 # 获取id标签值 2 element = driver.find_element_by_id("passwd-id") 3 # 获取name标签值 4 element = driver.find_element_by_name("user-name") 5 # 获取标签名值 6 element = driver.find_elements_by_tag_name("input") 7 # 也可以通过xpath来匹配 8 element = driver.find_element_by_xpath("//input[@id='passwd-id']") 9 10 find_element_by_id 11 find_elements_by_name 12 find_elements_by_xpath 13 find_elements_by_link_text 14 # 上下两行区别: 15 # partial 是部分的意思,可以定位a标签文本里的部分内容 16 find_elements_by_partial_link_text 17 find_elements_by_tag_name 18 find_elements_by_class_name 19 find_elements_by_css_selector
鼠标动作链
1 #导入 actionchains 类 2 from selenium.webdriver import actionchains 3 4 # 鼠标移动到 ac 位置 5 ac = driver.find_element_by_xpath('element') 6 actionchains(driver).move_to_element(ac).perform() 7 8 9 # 在 ac 位置单击 10 ac = driver.find_element_by_xpath("elementa") 11 actionchains(driver).move_to_element(ac).click(ac).perform() 12 13 # 在 ac 位置双击 14 ac = driver.find_element_by_xpath("elementb") 15 actionchains(driver).move_to_element(ac).double_click(ac).perform() 16 17 # 在 ac 位置右击 18 ac = driver.find_element_by_xpath("elementc") 19 actionchains(driver).move_to_element(ac).context_click(ac).perform() 20 21 # 在 ac 位置左键单击hold住 22 ac = driver.find_element_by_xpath('elementf') 23 actionchains(driver).move_to_element(ac).click_and_hold(ac).perform() 24 25 # 将 ac1 拖拽到 ac2 位置 26 ac1 = driver.find_element_by_xpath('elementd') 27 ac2 = driver.find_element_by_xpath('elemente') 28 actionchains(driver).drag_and_drop(ac1, ac2).perform()
填充表单
1 # 导入 select 类 2 from selenium.webdriver.support.ui import select 3 4 # 找到 name 的选项卡 5 select = select(driver.find_element_by_name('status')) 6 7 # 8 select.select_by_index(1) 9 select.select_by_value("0") 10 select.select_by_visible_text(u"未审核")
以上是三种选择下拉框的方式,它可以根据索引来选择,可以根据值来选择,可以根据文字来选择。注意:
- index 索引从 0 开始
- value是option标签的一个属性值,并不是显示在下拉框中的值
- visible_text是在option标签文本的值,是显示在下拉框的值
- 全部取消选择
alert = driver.switch_to_alert()
页面切换
一个浏览器肯定会有很多窗口,所以我们肯定要有方法来实现窗口的切换。切换窗口的方法如下:
driver.switch_to.window("this is window name")
也可以使用 window_handles 方法来获取每个窗口的操作对象。例如:
for handle in driver.window_handles: driver.switch_to_window(handle)
页面前进和后退
driver.forward() #前进 driver.back() # 后退
cookies
1 for cookie in driver.get_cookies(): 2 print "%s -> %s" % (cookie['name'], cookie['value']) 3 4 # by name 5 driver.delete_cookie("cookiename") 6 7 # all 8 driver.delete_all_cookies()
页面等待
隐式等待是等待特定的时间,显式等待是指定某一条件直到这个条件成立时继续执行。
显式等待
显式等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常了
1 from selenium import webdriver 2 from selenium.webdriver.common.by import by 3 # webdriverwait 库,负责循环等待 4 from selenium.webdriver.support.ui import webdriverwait 5 # expected_conditions 类,负责条件出发 6 from selenium.webdriver.support import expected_conditions as ec 7 8 driver = webdriver.chrome() 9 driver.get("http://www.xxxxx.com/loading") 10 try: 11 # 页面一直循环,直到 id="mydynamicelement" 出现 12 element = webdriverwait(driver, 10).until( 13 ec.presence_of_element_located((by.id, "mydynamicelement")) 14 ) 15 finally: 16 driver.quit()
如果不写参数,程序默认会 0.5s 调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回
下面是一些内置的等待条件,你可以直接调用这些条件,而不用自己写某些等待条件了
title_is
title_contains
presence_of_element_located
visibility_of_element_located
visibility_of
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value
frame_to_be_available_and_switch_to_it
invisibility_of_element_located
element_to_be_clickable – it is displayed and enabled.
staleness_of
element_to_be_selected
element_located_to_be_selected
element_selection_state_to_be
element_located_selection_state_to_be
alert_is_present
隐式等待
不设置,默认等待时间为0,单位为秒
1 from selenium import webdriver 2 3 driver = webdriver.chrome() 4 driver.implicitly_wait(10) # seconds 5 driver.get("http://www.xxxxx.com/loading") 6 mydynamicelement = driver.find_element_by_id("mydynamicelement")
def __del__(self):
'''调用内建的稀构方法,在程序退出的时候自动调用
类似的还可以在文件打开的时候调用close,数据库链接的断开
'''
self.driver.quit()
# 默认这个方法是对象使用后自动销毁对象用的,在这里修改为对象使用后关闭浏览器
tesseract与pytesseract
1 import pytesseract 2 from pil import image 3 4 image = image.open('test.jpg') 5 text = pytesseract.image_to_string(image) 6 print text 7 对图片进行阈值过滤和降噪处理 8 from pil import image 9 import subprocess 10 11 def cleanfile(filepath, newfilepath): 12 image = image.open(filepath) 13 14 # 对图片进行阈值过滤(低于143的置为黑色,否则为白色) 15 image = image.point(lambda x: 0 if x < 143 else 255) 16 # 重新保存图片 17 image.save(newfilepath) 18 19 # 调用系统的tesseract命令对图片进行ocr识别 20 subprocess.call(["tesseract", newfilepath, "output"]) 21 22 # 打开文件读取结果 23 with open("output.txt", 'r') as f: 24 print(f.read()) 25 26 if __name__ == "__main__": 27 cleanfile("text2.png", "text2clean.png")
scrapy框架
保存数据
scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,,命令如下:
# json格式,默认为unicode编码
scrapy crawl itcast -o teachers.json
# json lines格式,默认为unicode编码
scrapy crawl itcast -o teachers.jsonl
# csv 逗号表达式,可用excel打开
scrapy crawl itcast -o teachers.csv
# xml格式
scrapy crawl itcast -o teachers.xml
元素选取
- contains的用法,or的用法,last()的含义
response.xpath('//*[contains(@class,"odd") or contains(@class,"even")]/td[last()]/text()').extract()
0-1000随意设置,数值越低,组件的优先级越高
parse()方法的工作机制
- 因为使用的yield,而不是return。parse函数将会被当做一个生成器使用。scrapy会逐一获取parse方法中生成的结果,并判断该结果是一个什么样的类型
- 如果是request则加入爬取队列,如果是item类型则使用pipeline处理,其他类型则返回错误信息
- scrapy取到第一部分的request不会立马就去发送这个request,只是把这个request放到队列里,然后接着从生成器里获取
- 取尽第一部分的request,然后再获取第二部分的item,取到item了,就会放到对应的pipeline里处理
- parse()方法作为回调函数(callback)赋值给了request,指定parse()方法来处理这些请求 scrapy.request(url, callback=self.parse)
- request对象经过调度,执行生成 scrapy.http.response()的响应对象,并送回给parse()方法,直到调度器中没有request(递归的思路)
- 取尽之后,parse()工作结束,引擎再根据队列和pipelines中的内容去执行相应的操作
- 程序在取得各个页面的items前,会先处理完之前所有的request队列里的请求,然后再提取item
- 这一切的一切,scrapy引擎和调度器将负责到底
crawlspiders
rule
crawlspider使用rules来决定爬虫的爬取规则,并将匹配后的url请求提交给引擎。所以在正常情况下,crawlspider不需要单独手动返回请求了
- link_extractor:是一个link extractor对象,用于定义需要提取的链接
- callback: 从link_extractor中每获取到链接时,参数所指定的值作为回调函数,该回调函数接受一个response作为其第一个参数
注意:当编写爬虫规则时,避免使用parse作为回调函数。由于crawlspider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败
- follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为none,follow 默认设置为true ,否则默认为false
- process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤
- process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会调用该函数。 (用来过滤request)
request和response
发送post请求
- 可以使用 yield scrapy.formrequest(url, formdata, callback)方法发送post请求
- 如果希望程序执行一开始就发送post请求,可以重写spider类的start_requests(self) 方法,并且不再调用start_urls里的url
1 class myspider(scrapy.spider): 2 # start_urls = ["http://www.example.com/"] 3 4 def start_requests(self): 5 url = 'http://www.renren.com/plogin.do' 6 7 # formrequest 是scrapy发送post请求的方法 8 yield scrapy.formrequest( 9 url = url, 10 formdata = {"email" : "mr_mao_hacker@163.com", "password" : "axxxxxxxe"}, 11 callback = self.parse_page 12 ) 13 def parse_page(self, response): 14 # do something
模拟登陆
- 使用formrequest.from_response()方法模拟用户登录
- 通常网站通过 实现对某些表单字段(如数据或是登录界面中的认证令牌等)的预填充
- 使用scrapy抓取网页时,如果想要预填充或重写像用户名、用户密码这些表单字段, 可以使用 formrequest.from_response() 方法实现
1 import scrapy 2 3 class loginspider(scrapy.spider): 4 name = 'example.com' 5 start_urls = ['http://www.example.com/users/login.php'] 6 7 def parse(self, response): 8 return scrapy.formrequest.from_response( 9 response, 10 formdata={'username': 'john', 'password': 'secret'}, 11 callback=self.after_login 12 ) 13 14 def after_login(self, response): 15 # check login succeed before going on 16 if "authentication failed" in response.body: 17 self.log("login failed", level=log.error) 18 return
中间件
防止爬虫被反
- 动态设置user-agent(随机切换user-agent,模拟不同用户的浏览器信息)
- 禁用cookies(也就是不启用cookies middleware,不向server发送cookies,有些网站通过cookie的使用发现爬虫行为)
- 可以通过cookies_enabled 控制 cookiesmiddleware 开启或关闭
- 设置延迟下载(防止访问过于频繁,设置为 2秒 或更高)
- google cache 和 baidu cache:如果可能的话,使用谷歌/百度等搜索引擎服务器页面缓存获取页面数据
- 使用ip地址池:vpn和代理ip,现在大部分网站都是根据ip来ban的
- 使用 crawlera(专用于爬虫的代理组件),正确配置和设置下载中间件后,项目所有的request都是通过crawlera发出
downloader_middlewares = {
'scrapy_crawlera.crawleramiddleware': 600
}
crawlera_enabled = true
crawlera_user = '注册/购买的userkey'
crawlera_pass = '注册/购买的password'
下载中间件
1 # middlewares.py 2 3 #!/usr/bin/env python 4 # -*- coding:utf-8 -*- 5 6 import random 7 import base64 8 9 from settings import user_agents 10 from settings import proxies 11 12 # 随机的user-agent 13 class randomuseragent(object): 14 def process_request(self, request, spider): 15 useragent = random.choice(user_agents) 16 17 request.headers.setdefault("user-agent", useragent) 18 19 class randomproxy(object): 20 def process_request(self, request, spider): 21 proxy = random.choice(proxies) 22 23 if proxy['user_passwd'] is none: 24 # 没有代理账户验证的代理使用方式 25 request.meta['proxy'] = "http://" + proxy['ip_port'] 26 else: 27 # 对账户密码进行base64编码转换 28 base64_userpasswd = base64.b64encode(proxy['user_passwd']) 29 # 对应到代理服务器的信令格式里 30 request.headers['proxy-authorization'] = 'basic ' + base64_userpasswd 31 request.meta['proxy'] = "http://" + proxy['ip_port']
为什么http代理要使用base64编码: http代理的原理很简单,就是通过http协议与代理服务器建立连接,协议信令中包含要连接到的远程主机的ip和端口号,如果有需要身份验证的话还需要加上授权信息,服务器收到信令后首先进行身份验证,通过后便与远程主机建立连接,连接成功之后会返回给客户端200,表示验证通过,就这么简单,下面是具体的信令格式:
settings
- bot_name
- 默认: 'scrapybot'
- 当您使用 startproject 命令创建项目时其也被自动赋值
- concurrent_items
- 默认: 100
- item processor(即 item pipeline) 同时处理(每个response的)item的最大值
- concurrent_requests
- 默认: 16
- scrapy downloader 并发请求(concurrent requests)的最大值
- default_request_headers
- 默认: 如下
{
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'accept-language': 'en',
}
scrapy http request使用的默认header。
- depth_limit
- 默认: 0
- 爬取网站最大允许的深度(depth)值。如果为0,则没有限制
- download_delay
- 默认: 0
- 下载器在下载同一个网站下一个页面前需要等待的时间。该选项可以用来限制爬取速度, 减轻服务器压力。同时也支持小数
- download_delay = 0.25 # 250 ms of delay
- 默认情况下,scrapy在两个请求间不等待一个固定的值, 而是使用0.5到1.5之间的一个随机值 * download_delay 的结果作为等待间隔
- download_timeout
- 默认: 180
- 下载器超时时间(单位: 秒)
- item_pipelines
- 默认: {}
- 保存项目中启用的pipeline及其顺序的字典。该字典默认为空,值(value)任意,不过值(value)习惯设置在0-1000范围内,值越小优先级越高
item_pipelines = {
'myspider.pipelines.somethingpipeline': 300,
'myspider.pipelines.itcastjsonpipeline': 800,
}
- log_enabled
- 默认: true
- 是否启用logging
- log_encoding
- 默认: 'utf-8'
- logging使用的编码
- log_level
- 默认: 'debug'
- log的最低级别。可选的级别有: critical、 error、warning、info、debug
- user_agent
- 默认: "scrapy/version (+http://scrapy.org)"
- 爬取的默认user-agent,除非被覆盖
- proxies: 代理设置
- cookies_enabled = false
- 禁用cookies
scrapy-redis
scrapy-redis提供了下面四种组件(components):(四种组件意味着这四个模块都要做相应的修改)
- scheduler
- duplication filter
- item pipeline
- base spider
scrapy-redis的总体思路
- 这个工程通过重写scheduler和spider类,实现了调度、spider启动和redis的交互
- 实现新的dupefilter和queue类,达到了判重和调度容器和redis的交互,因为每个主机上的爬虫进程都访问同一个redis数据库,所以调度和判重都统一进行统一管理,达到了分布式爬虫的目的
- 当spider被初始化时,同时会初始化一个对应的scheduler对象,这个调度器对象通过读取settings,配置好自己的调度容器queue和判重工具dupefilter
- 每当一个spider产出一个request的时候,scrapy内核会把这个reuqest递交给这个spider对应的scheduler对象进行调度,scheduler对象通过访问redis对request进行判重,如果不重复就把他添加进redis中的调度池
- 当调度条件满足时,scheduler对象就从redis的调度池中取出一个request发送给spider,让他爬取
- 当spider爬取的所有暂时可用url之后,scheduler发现这个spider对应的redis的调度池空了,于是触发信号spider_idle,spider收到这个信号之后,直接连接redis读取strart url池,拿去新的一批url入口,然后再次重复上边的工作
请使用手机"扫一扫"x