爬虫原理与数据抓取
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 = (账户名, 密码)
- import requests
- auth=('test', '123456')
- response = requests.get('http://192.168.199.107', auth = auth)
- print (response.text)
- Cookies
- import requests
- response = requests.get("http://www.baidu.com/")
- # 7\. 返回 CookieJar 对象:
- cookiejar = response.cookies
- # 8\. 将 CookieJar 转为字典:
- cookiedict = requests.utils.dict_from_cookiejar(cookiejar)
- print (cookiejar)
- 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 对象后写入文
- import JSON
- listStr = [{"city": "北京"}, {"name": "大刘"}]
- JSON.dump(listStr, open("listStr.json","w"), ensure_ascii=False)
- dictStr = {"city": "北京", "name": "大刘"}
- JSON.dump(dictStr, open("dictStr.json","w"), ensure_ascii=False)
- JSON.load()
读取文件中 JSON 形式的字符串元素 转化成 python 类型
- import JSON
- strList = JSON.load(open("listStr.json"))
- print strList
- # [{u'city': u'\u5317\u4eac'}, {u'name': u'\u5927\u5218'}]
- strDict = JSON.load(open("dictStr.json"))
- print strDict
- # {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
- print soup.name
- # [document] #soup 对象本身比较特殊, 它的 name 即为 [document]
- print soup.head.name
- # head #对于其他内部标签, 输出的值便为标签本身的名称
- print soup.p.attrs
- # {'class': ['title'], 'name': 'dromouse'}
- # 在这里, 我们把 p 标签的所有属性打印输出了出来, 得到的类型是一个字典.
- print soup.p['class'] # soup.p.get('class')
- # ['title'] #还可以利用 get 方法, 传入属性的名称, 二者是等价的
- soup.p['class'] = "newClass"
- print soup.p # 可以对这些属性和内容等等进行修改
- # <p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>
- del soup.p['class'] # 还可以对这个属性进行删除
- print soup.p
- # <p name="dromouse"><b>The Dormouse's story</b></p>
- print soup.p.string
- # The Dormouse's story
- print type(soup.p.string)
- # In [13]: <class 'bs4.element.NavigableString'>
- # Comment 对象是一个特殊类型的 NavigableString 对象, 其输出的内容不包括注释符号
- print soup.a
- # <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
- print soup.a.string
- # Elsie
- print type(soup.a.string)
- # <class 'bs4.element.Comment'>
- # tag 的 .content 属性可以将 tag 的子节点以列表的方式输出
- print soup.head.contents
- #[<title>The Dormouse's story</title>]
- print soup.head.contents[0]
- #<title>The Dormouse's story</title>
- # .children 返回的是一个 list 生成器对象, 通过遍历获取所有子节点
- print soup.head.children
- #<listiterator object at 0x7f71457f5710>
- for child in soup.body.children:
- print child
- # 所有子孙节点: .descendants 属性
.contents 和 .children 属性仅包含 tag 的直接子节点,.descendants 属性可以对所有 tag 的子孙节点进行递归循环, 和 children 类似, 我们也需要遍历获取其中的内容
- for child in soup.descendants:
- print child
动态 HTML 处理和机器图像识别
- Selenium
- # 调用键盘按键操作时需要引入的 Keys 包
- from selenium.webdriver.common.keys import Keys
- # 生成当前页面快照并保存
- driver.save_screenshot("baidu.png")
- # 打印网页渲染后的源代码
- print driver.page_source
- # 获取当前页面 Cookie
- print driver.get_cookies()
- # ctrl+a 全选输入框内容
- driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
- # ctrl+x 剪切输入框内容
- driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')
- # 模拟 Enter 回车键
- driver.find_element_by_id("su").send_keys(Keys.RETURN)
- # 获取当前 url
- print driver.current_url
- # 关闭当前页面, 如果只有一个页面, 会关闭浏览器
- # driver.close()
- # 关闭浏览器
- driver.quit()
页面操作
- # 获取 id 标签值
- element = driver.find_element_by_id("passwd-id")
- # 获取 name 标签值
- element = driver.find_element_by_name("user-name")
- # 获取标签名值
- element = driver.find_elements_by_tag_name("input")
- # 也可以通过 XPath 来匹配
- element = driver.find_element_by_xpath("//input[@id='passwd-id']")
- find_element_by_id
- find_elements_by_name
- find_elements_by_xpath
- find_elements_by_link_text
- # 上下两行区别:
- # partial 是部分的意思, 可以定位 a 标签文本里的部分内容
- find_elements_by_partial_link_text
- find_elements_by_tag_name
- find_elements_by_class_name
- find_elements_by_css_selector
鼠标动作链
- # 导入 ActionChains 类
- from selenium.webdriver import ActionChains
- # 鼠标移动到 ac 位置
- ac = driver.find_element_by_xpath('element')
- ActionChains(driver).move_to_element(ac).perform()
- # 在 ac 位置单击
- ac = driver.find_element_by_xpath("elementA")
- ActionChains(driver).move_to_element(ac).click(ac).perform()
- # 在 ac 位置双击
- ac = driver.find_element_by_xpath("elementB")
- ActionChains(driver).move_to_element(ac).double_click(ac).perform()
- # 在 ac 位置右击
- ac = driver.find_element_by_xpath("elementC")
- ActionChains(driver).move_to_element(ac).context_click(ac).perform()
- # 在 ac 位置左键单击 hold 住
- ac = driver.find_element_by_xpath('elementF')
- ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()
- # 将 ac1 拖拽到 ac2 位置
- ac1 = driver.find_element_by_xpath('elementD')
- ac2 = driver.find_element_by_xpath('elementE')
- ActionChains(driver).drag_and_drop(ac1, ac2).perform()
填充表单
- # 导入 Select 类
- from selenium.webdriver.support.ui import Select
- # 找到 name 的选项卡
- select = Select(driver.find_element_by_name('status'))
- #
- select.select_by_index(1)
- select.select_by_value("0")
- select.select_by_visible_text(u"未审核")
以上是三种选择下拉框的方式, 它可以根据索引来选择, 可以根据值来选择, 可以根据文字来选择. 注意:
index 索引从 0 开始
value 是 option 标签的一个属性值, 并不是显示在下拉框中的值
visible_text 是在 option 标签文本的值, 是显示在下拉框的值
全部取消选择
alert = driver.switch_to_alert()
页面切换
一个浏览器肯定会有很多窗口, 所以我们肯定要有方法来实现窗口的切换. 切换窗口的方法如下:
driver.switch_to.Windows("this is window name")
也可以使用 window_handles 方法来获取每个窗口的操作对象. 例如:
- for handle in driver.window_handles:
- driver.switch_to_window(handle)
页面前进和后退
- driver.forward() #前进
- driver.back() # 后退
- Cookies
- for cookie in driver.get_cookies():
- print "%s -> %s" % (cookie['name'], cookie['value'])
- # By name
- driver.delete_cookie("CookieName")
- # all
- driver.delete_all_cookies()
页面等待
隐式等待是等待特定的时间, 显式等待是指定某一条件直到这个条件成立时继续执行.
显式等待
显式等待指定某个条件, 然后设置最长等待时间. 如果在这个时间还没有找到元素, 那么便会抛出异常了
- from selenium import webdriver
- from selenium.webdriver.common.by import By
- # WebDriverWait 库, 负责循环等待
- from selenium.webdriver.support.ui import WebDriverWait
- # expected_conditions 类, 负责条件出发
- from selenium.webdriver.support import expected_conditions as EC
- driver = webdriver.Chrome()
- driver.get("http://www.xxxxx.com/loading")
- try:
- # 页面一直循环, 直到 id="myDynamicElement" 出现
- element = WebDriverWait(driver, 10).until(
- EC.presence_of_element_located((By.ID, "myDynamicElement"))
- )
- finally:
- 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, 单位为秒
- from selenium import webdriver
- driver = webdriver.Chrome()
- driver.implicitly_wait(10) # seconds
- driver.get("http://www.xxxxx.com/loading")
- myDynamicElement = driver.find_element_by_id("myDynamicElement")
- def __del__(self):
- '''调用内建的稀构方法, 在程序退出的时候自动调用
- 类似的还可以在文件打开的时候调用 close, 数据库链接的断开
- '''
- self.driver.quit()
- # 默认这个方法是对象使用后自动销毁对象用的, 在这里修改为对象使用后关闭浏览器
Tesseract 与 pytesseract
- import pytesseract
- from PIL import Image
- image = Image.open('test.jpg')
- text = pytesseract.image_to_string(image)
- print text
对图片进行阈值过滤和降噪处理
- from PIL import Image
- import subprocess
- def cleanFile(filePath, newFilePath):
- image = Image.open(filePath)
- # 对图片进行阈值过滤(低于 143 的置为黑色, 否则为白色)
- image = image.point(lambda x: 0 if x < 143 else 255)
- # 重新保存图片
- image.save(newFilePath)
- # 调用系统的 tesseract 命令对图片进行 OCR 识别
- subprocess.call(["tesseract", newFilePath, "output"])
- # 打开文件读取结果
- with open("output.txt", 'r') as f:
- print(f.read())
- if __name__ == "__main__":
- 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
- class mySpider(scrapy.Spider):
- # start_urls = ["http://www.example.com/"]
- def start_requests(self):
- url = 'http://www.renren.com/PLogin.do'
- # FormRequest 是 Scrapy 发送 POST 请求的方法
- yield scrapy.FormRequest(
- url = url,
- formdata = {"email" : "mr_mao_hacker@163.com", "password" : "axxxxxxxe"},
- callback = self.parse_page
- )
- def parse_page(self, response):
- # do something
模拟登陆
使用 FormRequest.from_response()方法模拟用户登录
通常网站通过 实现对某些表单字段 (如数据或是登录界面中的认证令牌等) 的预填充
使用 Scrapy 抓取网页时, 如果想要预填充或重写像用户名, 用户密码这些表单字段, 可以使用 FormRequest.from_response() 方法实现
- import scrapy
- class LoginSpider(scrapy.Spider):
- name = 'example.com'
- start_urls = ['http://www.example.com/users/login.php']
- def parse(self, response):
- return scrapy.FormRequest.from_response(
- response,
- formdata={'username': 'john', 'password': 'secret'},
- callback=self.after_login
- )
- def after_login(self, response):
- # check login succeed before going on
- if "authentication failed" in response.body:
- self.log("Login failed", level=log.ERROR)
- 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'
下载中间件
- # middlewares.py
- #!/usr/bin/env python
- # -*- coding:utf-8 -*-
- import random
- import base64
- from settings import USER_AGENTS
- from settings import PROXIES
- # 随机的 User-Agent
- class RandomUserAgent(object):
- def process_request(self, request, spider):
- useragent = random.choice(USER_AGENTS)
- request.headers.setdefault("User-Agent", useragent)
- class RandomProxy(object):
- def process_request(self, request, spider):
- proxy = random.choice(PROXIES)
- if proxy['user_passwd'] is None:
- # 没有代理账户验证的代理使用方式
- request.meta['proxy'] = "http://" + proxy['ip_port']
- else:
- # 对账户密码进行 base64 编码转换
- base64_userpasswd = base64.b64encode(proxy['user_passwd'])
- # 对应到代理服务器的信令格式里
- request.headers['Proxy-Authorization'] = 'Basic' + base64_userpasswd
- 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
来源: https://www.cnblogs.com/siplips/p/9881199.html