Python爬虫——Scrapy模块

Scrapy

Scrapy,Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。
Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。
Scrap,是碎片的意思,这个Python的爬虫框架叫Scrapy。

Scrapy主要包括了以下组件:

  • 引擎(Scrapy)
    用来处理整个系统的数据流处理, 触发事务(框架核心)

  • 调度器(Scheduler)
    用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

  • 下载器(Downloader)
    用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)

  • 爬虫(Spiders)
    爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面

  • 项目管道(Pipeline)
    负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

  • 下载器中间件(Downloader Middlewares)
    位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。

  • 爬虫中间件(Spider Middlewares)
    介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。

  • 调度中间件(Scheduler Middewares)
    介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

Scrapy运行流程如下:

  1. 引擎从调度器中取出一个链接(URL)用于接下来的抓取
  2. 引擎把URL封装成一个请求(Request)传给下载器

  3. 下载器把资源下载下来,并封装成应答包(Response)

  4. 爬虫解析Response

  5. 解析出实体(Item),则交给实体管道进行进一步的处理

  6. 解析出的是链接(URL),则把URL交给调度器等待抓取

安装教程

1 Linux
2       pip3 install scrapy
3 Windows
4       a. pip3 install wheel
5       b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
6       c. 进入下载目录,执行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
7       d. pip3 install scrapy
8       e. 下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/

新建项目

1 scrapy startproject xxxxx  xxxxx为新建的项目名称
2 scrapy genspider <name> <domain>
3 例如
4 scrapy genspider chouti chouti.com

操作项目

1 cd进入xxxxx的文件夹
2 
3 scrapy crawl 爬虫应用名称

新建Scrapy的目录内容

 

 1 project_name/
 2    scrapy.cfg
 3    project_name/
 4        __init__.py
 5        items.py
 6        pipelines.py
 7        settings.py
 8        spiders/
 9            __init__.py
10            project_name.py

 

下面解释一下文件

  • scrapy.cfg 项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中)

  • items.py 设置数据存储模板,用于结构化数据。

  • pipelines 数据处理行为,如:一般结构化的数据持久化

  • settings.py 配置文件,如:递归的层数、并发数,延迟下载等

  • spiders 爬虫目录,如:创建文件,编写爬虫规则

选择器的使用

 

1 from scrapy.selector import Selector, HtmlXPathSelector

选择器规则

nodeName    选取此节点的所有节点
/           从根节点选取
//          从匹配选择的当前节点选择文档中的节点,不考虑它们的位置
.           选择当前节点
..          选取当前节点的父节点
@           选取属性
*           匹配任何元素节点
@*          匹配任何属性节点
Node()      匹配任何类型的节点

具体语法

1 //div[id="Test"]                  #查找id为Test的标签
2 //a[re:test(@herf, '/all/hot/\d+')]      # 正则表达式查找a标签
3 .//h3/text()                             # h3标签的文本内容
4 .//div[@class='part1']//a[1]/@href       # a标签的href文本

extract_first() 返回一个字符串

extract()       返回一个数组

携带cookies

 

 1 # 方法一
 2 print(response.headers.getlist('Set-Cookie'))
 3 # 解析之后的cookie
 4 cookie_dict = {}
 5 cookie_jar = CookieJar()
 6 cookie_jar.extract_cookies(response, response.request)
 7 for k, v in cookie_jar._cookies.items():
 8     for i, j in v.items():
 9         for m, n in j.items():
10             self.cookie_dict[m] = n.value
11 print(self.cookie_dict)
12 
13 # 方法二,在Request对象添加meta={'cookiejar': True}
14 yield Request(url=url, callback=self.login, meta={'cookiejar': True})

 

请求头-可在下载中间件添加

 1 from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
 2 
 3 class MyAgent(UserAgentMiddleware):
 4     def __init__(self,user_agent=''):
 5         self.user_agent = user_agent
 6 
 7     def process_request(self,request,spider):
 8         request.headers.setdefault('Host','www.jianshu.com')
 9         request.headers.setdefault('User-Agent','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36')
10 
11 配置文件走一下
12 DOWNLOADER_MIDDLEWARES = {
13     'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware' : None,#必需 ,禁用默认的middleware
14     'Test.middlewares.MyAgent': 543,
15 }

 

去重

1 DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'
2 DUPEFILTER_DEBUG = False
3 JOBDIR = "保存范文记录的日志路径,如:/root/"  # 最终路径为 /root/requests.seen

自定义去重

 1 class RepeatUrl:
 2     def __init__(self):
 3         self.visited_url = set()
 4 
 5     @classmethod
 6     def from_settings(cls, settings):
 7         """
 8         初始化时,调用
 9         :param settings: 
10         :return: 
11         """
12         return cls()
13 
14     def request_seen(self, request):
15         """
16         检测当前请求是否已经被访问过
17         :param request: 
18         :return: True表示已经访问过;False表示未访问过
19         """
20         if request.url in self.visited_url:
21             return True
22         self.visited_url.add(request.url)
23         return False
24 
25     def open(self):
26         """
27         开始爬去请求时,调用
28         :return: 
29         """
30         print('open replication')
31 
32     def close(self, reason):
33         """
34         结束爬虫爬取时,调用
35         :param reason: 
36         :return: 
37         """
38         print('close replication')
39 
40     def log(self, request, spider):
41         """
42         记录日志
43         :param request: 
44         :param spider: 
45         :return: 
46         """
47         print('repeat', request.url)

 

爬虫中间件

 1 class SpiderMiddleware(object):
 2 
 3     def process_spider_input(self,response, spider):
 4         """
 5         下载完成,执行,然后交给parse处理
 6         :param response: 
 7         :param spider: 
 8         :return: 
 9         """
10         pass
11 
12     def process_spider_output(self,response, result, spider):
13         """
14         spider处理完成,返回时调用
15         :param response:
16         :param result:
17         :param spider:
18         :return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)
19         """
20         return result
21 
22     def process_spider_exception(self,response, exception, spider):
23         """
24         异常调用
25         :param response:
26         :param exception:
27         :param spider:
28         :return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline
29         """
30         return None
31 
32 
33     def process_start_requests(self,start_requests, spider):
34         """
35         爬虫启动时调用
36         :param start_requests:
37         :param spider:
38         :return: 包含 Request 对象的可迭代对象
39         """
40         return start_requests
爬虫中间件
 1 class DownMiddleware1(object):
 2     def process_request(self, request, spider):
 3         """
 4         请求需要被下载时,经过所有下载器中间件的process_request调用
 5         :param request: 
 6         :param spider: 
 7         :return:  
 8             None,继续后续中间件去下载;
 9             Response对象,停止process_request的执行,开始执行process_response
10             Request对象,停止中间件的执行,将Request重新调度器
11             raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
12         """
13         pass
14 
15 
16 
17     def process_response(self, request, response, spider):
18         """
19         spider处理完成,返回时调用
20         :param response:
21         :param result:
22         :param spider:
23         :return: 
24             Response 对象:转交给其他中间件process_response
25             Request 对象:停止中间件,request会被重新调度下载
26             raise IgnoreRequest 异常:调用Request.errback
27         """
28         print('response1')
29         return response
30 
31     def process_exception(self, request, exception, spider):
32         """
33         当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
34         :param response:
35         :param exception:
36         :param spider:
37         :return: 
38             None:继续交给后续中间件处理异常;
39             Response对象:停止后续process_exception方法
40             Request对象:停止中间件,request将会被重新调用下载
41         """
42         return None
下载中间件

设置代理

 1 from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware
 2     
 3     方式一:使用默认
 4         os.environ
 5         {
 6             http_proxy:http://root:woshiniba@192.168.11.11:9999/
 7             https_proxy:http://192.168.11.11:9999/
 8         }
 9     方式二:使用自定义下载中间件
10     
11     def to_bytes(text, encoding=None, errors='strict'):
12         if isinstance(text, bytes):
13             return text
14         if not isinstance(text, six.string_types):
15             raise TypeError('to_bytes must receive a unicode, str or bytes '
16                             'object, got %s' % type(text).__name__)
17         if encoding is None:
18             encoding = 'utf-8'
19         return text.encode(encoding, errors)
20         
21     class ProxyMiddleware(object):
22         def process_request(self, request, spider):
23             PROXIES = [
24                 {'ip_port': '111.11.228.75:80', 'user_pass': ''},
25                 {'ip_port': '120.198.243.22:80', 'user_pass': ''},
26                 {'ip_port': '111.8.60.9:8123', 'user_pass': ''},
27                 {'ip_port': '101.71.27.120:80', 'user_pass': ''},
28                 {'ip_port': '122.96.59.104:80', 'user_pass': ''},
29                 {'ip_port': '122.224.249.122:8088', 'user_pass': ''},
30             ]
31             proxy = random.choice(PROXIES)
32             if proxy['user_pass'] is not None:
33                 request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
34                 encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))
35                 request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)
36                 print "**************ProxyMiddleware have pass************" + proxy['ip_port']
37             else:
38                 print "**************ProxyMiddleware no pass************" + proxy['ip_port']
39                 request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
40 
41 配置文件添加
42 DOWNLOADER_MIDDLEWARES = {
43        'step8_king.middlewares.ProxyMiddleware': 500,
44     }
View Code

设置证书

 1 Https访问
 2     Https访问时有两种情况:
 3     1. 要爬取网站使用的可信任证书(默认支持)
 4         DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
 5         DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"
 6         
 7     2. 要爬取网站使用的自定义证书
 8         DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
 9         DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory"
10         
11         # https.py
12         from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
13         from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate)
14         
15         class MySSLFactory(ScrapyClientContextFactory):
16             def getCertificateOptions(self):
17                 from OpenSSL import crypto
18                 v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.key.unsecure', mode='r').read())
19                 v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read())
20                 return CertificateOptions(
21                     privateKey=v1,  # pKey对象
22                     certificate=v2,  # X509对象
23                     verify=False,
24                     method=getattr(self, 'method', getattr(self, '_ssl_method', None))
25                 )
证书

配置文件详解

 1 BOT_NAME = 'news'  # 爬虫的名字 会放在USER_AGENT中携带到请求里
 2 
 3 SPIDER_MODULES = ['news.spiders'] # 爬虫的路径
 4 NEWSPIDER_MODULE = 'news.spiders' # 爬虫的路径
 5 
 6 # USER_AGENT = 'news (+http://www.yourdomain.com)' # 同http请求的请求头中User-Agent
 7 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.3964.2 Safari/537.36'
 8 
 9 ROBOTSTXT_OBEY = True # 是否遵守爬虫的规则,如果遵守会检测网站中配置文件中是否允许爬虫
10 
11 CONCURRENT_REQUESTS = 32 # 请求并发数,默认并发16个请求
12 
13 DOWNLOAD_DELAY = 3  # 下载延迟,表示每个请求访问前的阻塞3秒
14 CONCURRENT_REQUESTS_PER_DOMAIN = 16 # 针对每个域名并发请求数
15 CONCURRENT_REQUESTS_PER_IP = 16     # 针对每个IP的并发请求数
16 
17 COOKIES_ENABLED = True # 是否爬取cookie, 默认为True
18 COOKIES_DEBUG = True # 开启cookie的debug模式,每次访问都会打印cookie
19 
20 
21 TELNETCONSOLE_ENABLED = False   # 监听6023端口,可以远程查看爬虫的情况"telnet 127.0.0.1"
22 
23 DEFAULT_REQUEST_HEADERS = {     # http请求的请求头
24   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
25   'Accept-Language': 'zh-CN,zh;q=0.8',
26 }
27 
28 SPIDER_MIDDLEWARES = {
29    'news.middlewares.NewsSpiderMiddleware': 543,
30 }
31 
32 EXTENSIONS = {  # 爬虫的信号注册到此选项
33    # 'scrapy.extensions.telnet.TelnetConsole': None,
34    'news.extensions.MyExtensions':300
35 }
36 
37 ITEM_PIPELINES = { # 爬虫的pipline注册到此选项
38    'news.pipelines.NewsPipeline': 300,
39 }
40 
41 SCHEDULER = 'scrapy.core.scheduler.Scheduler'  # 调度器队列
42 
43 DEPTH_LIMIT = 1     # 爬虫爬取深度
44 
45 DEPTH_PRIORITY = 0  # 1:广度优先,0:深度优先
46 
47 DUPEFILTER_CLASS = 'news.duplicatioe.MyDupeFilter' # url重复过滤器

 个人总结

  • 对于这次scrapy的初步了解,更多的去了解scrapy框架的源码,因为只有源码才是一切的起源
  • scrapy是一个分布式的爬虫框架,每爬取得到一个item就直接送到pipeline中进行处理,而不是等所有item都爬取完后再进行处理,这一点要注意。
  • 对于调度器和各种中间件,在setting配置文件要添加
  • Request是一个封装用户请求的类,在回调函数中yield该对象表示继续访问
  • HtmlXpathSelector用于结构化HTML代码并提供选择器功能
  • setting的数字越大,权重越大,先执行

转载于:https://www.cnblogs.com/Ojia/p/9289554.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值