中文文档:
scrapy 4 步:
-
新建项目:(scrapy startproject projectname):新建爬虫项目
-
创建爬虫:scrapy genspider spidername “http://www.xxx.cn/”
-
明确目标:(编写items.py):明确想要抓取的目标
-
制作爬虫:(spiders/xxspider.py):制作爬虫开始爬取的网页
-
编写pipeline.py,处理spider返回的item数据。写Pipeline函数
-
编写settings.py,启动管道组件ITEM_PIPELINES={},以及其他相关设置USER_AGENT,DEFAULT_REQUEST_HEADERS
-
执行爬虫
``` from scrapy import cmdline cmdline.execute(['scrapy','crawl','cib']) ```
创建爬虫
- scrapy genspider spidername “http://www.xxx.cn/”
- genspider:表示生成一个爬虫(默认是scrapy.Spider类)
- spidername:表示爬虫名(对应爬虫代码里的name参数)
- “http://www.xxx.cn/” :表示允许爬虫爬取的域范围
- spidername.py
- name= ‘’:爬虫的识别名称,唯一
- allow_domains=[] :搜索的域名范围,爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的url会被忽略
- start_urls=():爬取的url列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成
- parse(self,response):解析的方法,每个初始URL完成下载后将被调用,调用的时候传入每一个URL传回的Response对象来作为唯一参数,主要作用如下:
- 负责解析返回的网页数据(response.body),提取结构化数据(生成item)
- 生成需要下一页的URL请求
- start_requests(self):这个方法必须返回一个可迭代对象。该对象包含spider用于爬取(默认实现是使用start_urls的url)的第一个Request。当spider启动爬取并且为指定start_urls时,调用该方法
- log(self,message[,level,component]):使用scrapy.log.msg()方法记录(log)message
执行爬虫
- scrapy crawl spidername -o save_filename
- crawl:表示启动一个scrapy爬虫
- spidername:表示需要启动的爬虫名(对应爬虫代码里的name参数)
- -0 :表示输出到文件
- save_filename:表示保存文件的名称,,默认4种输出文件格式:json,jsonl,csv,xml
查看当前项目下的爬虫
- scrapy list
pipeline的一些典型应用
- 验证爬取的数据(检查item包含某些字段,比如说name)
- 数据查重(并丢弃)
- 将爬取结果保存到文件或者数据库中
class SomethingPiple(object):
def __init__(self):
# 可选实现,做参数初始化,比如打开文件操作f.open('xxx','w',edcoding='utf-8')
# doing something
def open_spider(self,spider):
# spider(Spider 对象) - 被关闭的spider
# 可选实现,当spider被开启时,这个方法被调用。
# 该方法和__init__方法功能基本相同。
def process_item(self,item,spider):
# item(Item对象) - 被爬取的item
# spider (Spider对象) - 爬取该item的spider
# 这个方法必须实现,每个item pipeline 组件都需要调用该方法
# 这个方法必须返回一个Item对象,被丢弃的item将不会被之后的pipeline组件处理
return item
def close_spider(self,spider):
# spider(Spider 对象) - 被关闭的spider
# 可选实现,当spider被关闭时,这个方法被调用。
# 比如关闭初始化打开的文件f.close()
启动Scrapy Shell:scrapy shell “www.baidu.com”
- response.headers:报头
- response.body:报体
- response.xpath()
Selectors选择器:
-
Selector有四个基本方法,最常用的是xpath:
- xpath():传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
- XPath表达式的例子及对应含义:
- /html/head/title:选择文档中标签内的
元素 - /html/head/title/text():选择文档中标签内的
元素的文字 - //td:选择所有的元素
- //div[@class=“mine”]:选择所有具有class="mine"属性的div元素
- /html/head/title:选择文档中标签内的
- XPath表达式的例子及对应含义:
- extract():序列化该结点为Unicode字符串,并返回list
- css():传入css表达式,返回该表达式所对应的所有节点的selector list列表,语法同BeautifulSoup4
- re():根据传入的正则表达式对数据进行提取,返回Unicode字符串list列表
- xpath():传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
注意
- xpath 返回的是一个列表
- xpath.extract():将xpath对象转换成Unicode字符串
- settings设置
- HTTPERROR_ALLOWED_CODES = [403, 500, 404]
- ROBOTSTXT_OBEY = False
- 下载中间件:
DOWNLOAD_DELAY = 2
RANDOMIZE_DOWNLOAD_DELAY = True
COOKIES_ENABLED = True
DOWNLOADER_MIDDLEWARES = {
'bank_info.middlewares.MyUserAgentMiddleware': 300,
'bank_info.middlewares.BankInfoDownloaderMiddleware': 543, # 值越小优先级越高
}
ITEM_PIPELINES = {
'bank_info.pipelines.BankInfoPipeline': 300, # 值越小优先级越高
}
scrapy 进阶
- 翻页功能:scrapy.follow(next_page,callback=self.parse) 会自动拼接url和next_page
- 抽取response中满足xpath规则的链接:LinkExtractor(restrict_xpath=‘xxxx’), links = link.extract_links(response)
- 要防止scrapy被ban,主要有以下几个策略:
- 动态设置user agent( 在middleware.py中随机选取user-agent,并把它赋值给request)
- 在settings开启UAMiddleware这个中间件:DOWNLOADER_MIDDLEWARES
-
import random class UAMiddleware(object): # 定义一个User-Agent的List ua_list = [ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 ', '(KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 'Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)', 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)', ] def process_request(self, request, spider): # 对request进行拦截 ua = random.choices(self.ua_list) # 使用random模块,随机在ua_list中选取User-Agent request.headers['User-Agent'] = ua # 把选取出来的User-Agent赋给request print(request.url) # 打印出request的url print(request.headers['User-Agent']) # 打印出request的headers def process_response(self, request, response, spider): # 对response进行拦截 return response def process_exception(self, request, exception, spider): # 对process_request方法传出来的异常进行处理 pass
- 禁用cookies : COOKIES_ENABLED=False
- 设置延迟下载 : DOWNLOAD_DELAY=2
- 使用Google cache
- 使用IP地址池(Tor project、VPN和代理IP)
- 使用Crawlera
- 动态设置user agent( 在middleware.py中随机选取user-agent,并把它赋值给request)
- scrapy中间件的分类:
- scrapy的中间件理论上有三种(Schduler Middleware,Spider Middleware,Downloader Middleware),在应用上一般有以下两种:
- 爬虫中间件Spider Middleware:可以添加代码来处理发送给 Spiders 的response及spider产生的item和request.
- 当蜘蛛传递请求和items给引擎的过程中,蜘蛛中间件可以对其进行处理(过滤出 URL 长度比 URLLENGTH_LIMIT 的 request。)
- 当引擎传递响应给蜘蛛的过程中,蜘蛛中间件可以对响应进行过滤(例如过滤出所有失败(错误)的 HTTP response)
- 下载器中间件Downloader Middleware:主要功能在请求到网页后,页面被下载时进行一些处理.(反爬策略都是部署在下载中间件的)
- 当引擎传递请求给下载器的过程中,下载中间件可以对请求进行处理 (例如增加http header信息,增加proxy信息等)
- 在下载器完成http请求,传递响应给引擎的过程中, 下载中间件可以对响应进行处理(例如进行gzip的解压等)
- 下载中间件三大函数:
- process_request(request, spider)——主要函数
- process_request() 必须返回其中之一: 返回 None 、返回一个 Response 对象、返回一个 Request 对象或raise IgnoreRequest
- 如果其返回 None: Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用, 该request被执行(其response被下载)
- 如果其返回Response 对象: Scrapy将不会调用任何其他的process_request()或 process_exception()方法,或相应的下载函数。其将返回该response,已安装的中间件的 process_response() 方法则会在每个response返回时被调用
- 如果其返回 Request对象 : Scrapy则会停止调用 process_request方法并重新调度返回的request,也就是把request重新返回,进入调度器重新入队列
- 如果其返回raise IgnoreRequest异常 : 则安装的下载中间件的 process_exception()方法 会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)
- process_response(request, response, spider)——主要函数
- process_response() 必须返回以下之一:返回一个Response 对象、 返回一个Request 对象或raise IgnoreRequest 异常
- 如果其返回一个 Response对象: (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理
- 如果其返回一个 Request对象: 则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样
- 如果其抛出一个IgnoreRequest异常 :则调用request的errback(Request.errback)。
- 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)
- process_exception(request, exception, spider)
- 如果其返回 None : Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception()方法,直到所有中间件都被调用完毕,则调用默认的异常处理
- 如果其返回一个 Response 对象: 相当于异常被纠正了,则已安装的中间件链的 process_response()方法被调用。Scrapy将不会调用任何其他中间件的 process_exception()方法
- 如果其返回一个 Request 对象: 则返回的request将会被重新调用下载。这将停止中间件的 process_exception() 方法执行,就如返回一个response的那样
- process_request(request, spider)——主要函数
- 爬虫中间件Spider Middleware:可以添加代码来处理发送给 Spiders 的response及spider产生的item和request.
- scrapy的中间件理论上有三种(Schduler Middleware,Spider Middleware,Downloader Middleware),在应用上一般有以下两种:
其他内置的downloader middleware
item | value |
---|---|
DefaultHeadersMiddleware | 将所有request的头设置为默认模式 |
DownloadTimeoutMiddleware | 设置request的timeout |
HttpAuthMiddleware | 对来自特定spider的request授权 |
HttpCacheMiddleware | 给request&response设置缓存策略 |
HttpProxyMiddleware | 给所有request设置http代理 |
RedirectMiddleware | 处理request的重定向 |
MetaRefreshMiddleware | 根据meta-refresh html tag处理重定向 |
RetryMiddleware | 失败重试策略 |
RobotsTxtMiddleware | robots封禁处理 |
UserAgentMiddleware | 支持user agent重写 |
- 代理
- 西刺免费代理网站:http://www.xicidaili.com/nn/
- 代理检测网站: www.httpbin.org/ip
# *-* coding:utf-8 *-*
import requests
from bs4 import BeautifulSoup
import lxml
from multiprocessing import Process, Queue
import random
import json
import time
import requests
class Proxies(object):
"""docstring for Proxies"""
def __init__(self, page=3):
self.proxies = []
self.verify_pro = []
self.page = page
self.headers = {
'Accept': '*/*',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'zh-CN,zh;q=0.8'
}
self.get_proxies()
self.get_proxies_nn()
def get_proxies(self):
page = random.randint(1, 10)
page_stop = page + self.page
while page < page_stop:
url = 'http://www.xicidaili.com/nt/%d' % page
html = requests.get(url, headers=self.headers).content
soup = BeautifulSoup(html, 'lxml')
ip_list = soup.find(id='ip_list')
for odd in ip_list.find_all(class_='odd'):
protocol = odd.find_all('td')[5].get_text().lower() + '://'
self.proxies.append(protocol + ':'.join([x.get_text() for x in odd.find_all('td')[1:3]]))
page += 1
def get_proxies_nn(self):
page = random.randint(1, 10)
page_stop = page + self.page
while page < page_stop:
url = 'http://www.xicidaili.com/nn/%d' % page
html = requests.get(url, headers=self.headers).content
soup = BeautifulSoup(html, 'lxml')
ip_list = soup.find(id='ip_list')
for odd in ip_list.find_all(class_='odd'):
protocol = odd.find_all('td')[5].get_text().lower() + '://'
self.proxies.append(protocol + ':'.join([x.get_text() for x in odd.find_all('td')[1:3]]))
page += 1
def verify_proxies(self):
# 没验证的代理
old_queue = Queue()
# 验证后的代理
new_queue = Queue()
print('verify proxy........')
works = []
for _ in range(15):
works.append(Process(target=self.verify_one_proxy, args=(old_queue, new_queue)))
for work in works:
work.start()
for proxy in self.proxies:
old_queue.put(proxy)
for work in works:
old_queue.put(0)
for work in works:
work.join()
self.proxies = []
while 1:
try:
self.proxies.append(new_queue.get(timeout=1))
except:
break
print('verify_proxies done!')
def verify_one_proxy(self, old_queue, new_queue):
while 1:
proxy = old_queue.get()
if proxy == 0: break
protocol = 'https' if 'https' in proxy else 'http'
proxies = {protocol: proxy}
try:
if requests.get('http://www.baidu.com', proxies=proxies, timeout=2).status_code == 200:
print('success %s' % proxy)
new_queue.put(proxy)
except:
print('fail %s' % proxy)
if __name__ == '__main__':
a = Proxies()
a.verify_proxies()
print(a.proxies)
proxie = a.proxies
with open('proxies.txt', 'w') as f:
for proxy in proxie:
f.write(proxy + '\n')
- 修改settings文件
DOWNLOADER_MIDDLEWARES = {
'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware':None,
'myproxies.middlewares.ProxyMiddleWare':125,
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware':None
}