scrapy初步解析源码即深度使用
scrapy深度爬虫
——编辑:大牧莫邪
本章内容
- 深度爬虫概述
- scrapy Spider实现的深度爬虫
- scrapy CrawlSpdier实现的深度爬虫
- 案例操作
课程内容
1. 深度爬虫概述
爬虫程序,主要是用与数据采集处理的一种网络程序,在操作过程中针对指定的url地址进行数据请求并根据需要采集数据,但是在实际项目开发过程中,经常会遇到目标url地址数量不明确的情况,如之前的章节中提到的智联招聘项目,不同的岗位搜索到的岗位数量不一定一致,也就意味着每个工作搜索到的工作岗位列表页面的数量不一定一致,爬虫工程师工作可能搜索到了10页,Django工作有可能都索到了25页数据,那么针对这样的数据要全部进行爬取,应该怎么处理呢?答案就是:深度爬虫
深度爬虫:针对其实url地址进行数据采集,在响应数据中进行数据筛选得到需要进行数据采集的下一波url地址,并将url地址添加到数据采集队列中进行二次爬取..以此类推,一致到所有页面的数据全部采集完成即可完成深度数据采集,这里的深度指代的就是url地址的检索深度。
深度爬虫可以通过不同的方式实现,在urllib2和requesets模块中通过轮询数据筛选得到目标url地址,然后进行循环爬取数据即可,在scrapy中主要通过两种方式进行处理:
- 通过Response对象的地址序列和Request对象的请求处理完成深度采集
- 通过CrawlSpider类型中的请求链接提取规则自动进行深度数据采集处理
2. Spider Request和Response完成数据深度采集
首先完成深度爬虫之前,先了解Scrapy框架底层的一些操作模式,Scrapy框架运行爬虫项目,默认调用并执行parse()函数进行数据的解析,但是此时已经由框架完成了请求解析调度和下载的过程,那么Scrapy到底做了哪些事情呢?
我们首先观察一下scrapy.Spider源代码
class Spider(object_ref):
"""Base class for scrapy spiders. All spiders must inherit from this
class.
""" name = None
custom_settings = None # 初始化函数,主要进行程序的名称、起始地址等数据初始化工作
def __init__(self, name=None, **kwargs):
if name is not None:
self.name = name
elif not getattr(self, 'name', None):
raise ValueError("%s must have a name" % type(self).__name__)
self.__dict__.update(kwargs)
if not hasattr(self, 'start_urls'):
self.start_urls = []
...
...
# 程序启动,发送请求的函数
def start_requests(self):
cls = self.__class__
# 默认没有重写直接调用,重写的时候根据子类重写的方式重新定义发送处理方式
# 默认情况下发送get请求获取数据,如果要发送Post请求可以重写start_reuqests函数进行请求的处理
if method_is_overridden(cls, Spider, 'make_requests_from_url'):
warnings.warn(
"Spider.make_requests_from_url method is deprecated; it "
"won't be called in future Scrapy releases. Please "
"override Spider.start_requests method instead (see %s.%s)." % (
cls.__module__, cls.__name__
),
)
for url in self.start_urls:
yield self.make_requests_from_url(url)
else:
# 没有重写该方法,直接根据初始地址包装请求对象发送请求
for url in self.start_urls:
yield Request(url, dont_filter=True)
我们可以从源代码中查看到,我们定义的爬虫处理类继承的scrapy.Spider类型中,对于初始化的name和start_urls初始地址进行了初始化,然后自动调用start_requests函数包装Request请求对象,然后通过协程调用的方法将请求交给调度器进行后续的处理
这里就需要了解请求对象中到底做了哪些事情?!
(1) Request对象 Request请求对象是scrapy框架中的核心对象,通过将字符串url地址包装成请求对象交给调度器进行调度管理,之后交给下载模块进行数据采集的操作
Request底层操作部分源码如下:
# scrapy中的Request请求对象
class Request(object_ref): # 默认构建时,method="GET"包装的是GET请求的采集方式
# 参数url:请求地址字符串
# 参数callback:请求的回调函数
# 参数headers:默认的请求头
# 参数body: 请求体
# 参数cookies:请求中包含的cookie对象
# 参数encoding:请求编码方式
def __init__(self, url, callback=None, method='GET', headers=None, body=None,
cookies=None, meta=None, encoding='utf-8', priority=,
dont_filter=False, errback=None, flags=None): self._encoding = encoding # this one has to be set first
self.method = str(method).upper()
self._set_url(url)
self._set_body(body)
assert isinstance(priority, int), "Request priority not an integer: %r" % priority
self.priority = priority if callback is not None and not callable(callback):
raise TypeError('callback must be a callable, got %s' % type(callback).__name__)
if errback is not None and not callable(errback):
raise TypeError('errback must be a callable, got %s' % type(errback).__name__)
assert callback or not errback, "Cannot use errback without a callback"
self.callback = callback
self.errback = errback self.cookies = cookies or {}
self.headers = Headers(headers or {}, encoding=encoding)
self.dont_filter = dont_filter self._meta = dict(meta) if meta else None
self.flags = [] if flags is None else list(flags)
那么在实际操作中,我们通过如下三点详细说明:
- 如何发送get请求
直接编写爬虫程序,定义strat_urls中的初始地址和爬虫的name名称,然后重写父类中的parse()函数即可,请求的发送默认就是get()方式进行数据采集:
import scrapy # 定义自己的爬虫处理类
class MySpider(scrapy.Spider):
# 定义爬虫名称
name = 'myspider'
# 定义初始化url地址列表
start_urls = ("http://www.baidu.com", )
# 定义域名限制
allowed_domains = ["baidu.com"] # 定义数据处理方式
def parse(self, response):
# 数据处理部分
pass
- 如何发送post请求
因为scarpy默认的Request是get方式发送请求,如果要通过post方式发送请求采集数据,需要重新编写start_requests()函数覆盖父类中的请求包装方式
import scrapy class MySpider(scrapy.Spider):
# 定义爬虫名称
name = 'myspider'
# 定义初始化url地址列表
start_urls = ("http://www.baidu.com", )
# 定义域名限制
allowed_domains = ["baidu.com"] # 重写父类请求初始化发送方式
def start_requests(self, response):
# 循环初始话地址,发送post请求
for url in self.start_urls:
yield scrapy.FormRequest(
url = url,
formdata = {post参数字典},
callback = self.parse_response,
) # 重新编写响应数据处理函数
def parse_response(self, response):
# 处理采集到的response数据
pass
同时,也可以通过响应对象构建一个POST请求重新发送,如下:
import scrapy class MySpider(scarpy.Spider): # 定义爬虫名称
name = 'myspider'
# 定义初始化url地址列表
start_urls = ("http://www.baidu.com", )
# 定义域名限制
allowed_domains = ["baidu.com"] # 重写父类请求初始化发送方式
def parse(self, response):
# 通过响应对象重新构建一个POST请求再次发送
return scrapy.FormRequest.from_response(
response,
formdata = {"post参数字典数据"},
callback = self.parse_response
) # 重新编写响应数据处理函数
def parse_response(self, response):
# 处理采集到的response数据
pass
(2) Response对象 Response对象在项目中的直接操作并不是很多,参考源代码如下:
# 部分代码
class Response(object_ref):
def __init__(self, url, status=, headers=None, body='', flags=None, request=None):
self.headers = Headers(headers or {})
self.status = int(status) # 响应码
self._set_body(body) # 响应体
self._set_url(url) # 响应url
self.request = request # 请求对象
self.flags = [] if flags is None else list(flags) @property
def meta(self):
try:
return self.request.meta
except AttributeError:
raise AttributeError("Response.meta not available, this response " \
"is not tied to any request")
(3)案例操作:模拟CSDN登录
- 创建爬虫项目
scrapy startproject csdnspider
- 在csdnspider/csdnspider/spiders/目录中创建csdnspider.py文件,创建爬虫类如下:
# coding:utf- import scrapy class CsdnSpider(scrapy.Spider):
'''
CSDN登录爬虫处理类
'''
# 爬虫名称
name = "cs"
# 初始登录地址
start_urls = ["https://passport.csdn.net/account/login"] def parse(self, response): # 匹配登录流水号
lt = response.xpath("//form[@id='fm1']/input[@type='hidden']/@value").extract()[] # 发送post请求完成登录
return scrapy.FormRequest.from_response(
response,
formdata = {
"username": "",
"password": "DAMUpython2016",
"lt": lt,
# "execution": "e2s1",
# "_eventId": "submit"
},
callback=self.parse_response
) def parse_response(self, response):
# 得到登录后的数据,进行后续处理
with open("csdn.html", "w") as f:
f.write(response.body)
(4). 深度采集数据:爬取智联某工作岗位所有页面工作数据
- 创建爬虫程序
scrapy startproject zlspider
- 分析请求,定义Item对象
# -*- coding: utf- -*- # Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html import scrapy class ZhilianItem(scrapy.Item):
'''
定义采集数据的类型,该类型中,会封装采集到的数据
继承scrapy.Item类型,scrapy框架才会调用内建函数继续自动化操作
'''
# 通过scrapy.Field()定义属性字段,每个字段都是采集数据的一部分
job_name = scrapy.Field()
company = scrapy.Field()
salary = scrapy.Field()
创建数据库,定义数据表,用于存储数据
# 创建数据库
DROP DATABASE py1709_spider;
CREATE DATABASE py1709_spider DEFAULT CHARSET 'utf8'; USE py1709_spider; # 创建数据表
CREATE TABLE jobs(
id INT AUTO_INCREMENT PRIMARY KEY,
job_name VARCHAR(),
company VARCHAR(),
salary VARCHAR()
);
SELECT COUNT() FROM jobs;
SELECT * FROM jobs;
TRUNCATE TABLE jobs;
- 开发爬虫程序,通过请求对象的自定义包装,完成请求链接[分页连接]跟踪爬取
在zlspider/zlspider/spider/文件夹中,创建zhilianspider.py文件,编辑爬虫程序如下:
# coding:utf- # 引入scrapy模块
import scrapy from ..items import ZhilianItem class ZhilianSpider(scrapy.Spider):
'''
智联招聘数据采集爬虫程序
需要继承scrapy.Spider类型,让scrapy负责调度爬虫程序进行数据的采集
'''
# name属性:爬虫名称
name = "zl"
# allowed_domains属性:限定采集数据的域名
allowed_domains = ["zhaopin.com"]
# 起始url地址
start_urls = [
#"http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%E5%8C%97%E4%BA%AC&kw=%E7%88%AC%E8%99%AB&sm=0&sg=cab76822e6044ff4b4b1a907661851f9&p=1",
"http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%E5%8C%97%E4%BA%AC%2b%E4%B8%8A%E6%B5%B7%2b%E5%B9%BF%E5%B7%9E%2b%E6%B7%B1%E5%9C%B3&kw=python&isadv=0&sg=7cd76e75888443e6b906df8f5cf121c1&p=1",
] def parse(self, response):
'''
采集的数据解析函数(响应数据解析函数)
主要用于进行响应数据的筛选:筛选目标数据分装成Item对象
:param response:
:return:
''' # # 再次从响应中获取要进行下一次爬取的url地址[其他页面请求]
# next_page = response.xpath("//div[@class='pagesDown']/ul/li/a/@href").extract()
# # 循环处理请求
# for page in next_page:
# page = response.urljoin(page)
# # 重新发起请求采集下一组url地址的数据[第一个参数:发起的请求地址,第二个参数:请求数据一旦被采集~交个哪个函数进行处理]
# yield scrapy.Request(page, callback=self.parse_response)
url = response.urljoin(self.start_urls[])
yield scrapy.Request(url, callback=self.parse_response) def parse_response(self, response):
# 筛选得到工作列表
job_list = response.xpath("//div[@id='newlist_list_content_table']/table[position()>1]/tr[1]")
# 循环获取采集的字段信息
for job in job_list:
# 岗位名称
job_name = job.xpath("td[@class='zwmc']/div/a").xpath("string(.)").extract()[]
# 公司名称
company = job.xpath("td[@class='gsmc']/a").xpath("string(.)").extract()[]
# 薪水
salary = job.xpath("td[@class='zwyx']").xpath("string(.)").extract()[] # 封装成item对象
item = ZhilianItem()
item['job_name'] = job_name
item['company'] = company
item['salary'] = salary # 通过协程的方式移交给pipeline进行处理
yield item
# 再次从响应中获取要进行下一次爬取的url地址[其他页面请求]
next_page = response.xpath("//div[@class='pagesDown']/ul/li/a/@href").extract()
# 循环处理请求
for page in next_page:
page = response.urljoin(page)
# 重新发起请求采集下一组url地址的数据[第一个参数:发起的请求地址,第二个参数:请求数据一旦被采集~交个哪个函数进行处理]
yield scrapy.Request(page, callback=self.parse_response)
运行测试程序 在终端命令行窗口中,运行程序
scrapy crawl zl
查看数据库中的数据记录
备注:在这样的深度采集数据时,首页数据很有可能会重复,所以,将数据解析函数分成了两个步骤执行,第一步通过parse()函数处理首页地址增加到response.urljoin()中,然后通过parse_response()函数进行实际的数据采集工作,达到首页数据去重的目的!
3. Spider CrawlSpider完成数据深度采集
Scrapy框架针对深度爬虫,提供了一种深度爬虫的封装类型scrapy.CrawlSpider,我们自己定义开发的爬虫处理类需要继承该类型,才能使用scrapy提供封装的各项深度爬虫的功能
scrapy.CrawlSpider是从scrapy.Spider继承并进行功能扩展的类型,在该类中,通过定义Url地址的提取规则,跟踪连接地址,从已经采集得到的响应数据中继续提取符合规则的地址进行跟踪爬取数据
部分源代码如下:
class CrawlSpider(Spider):
rules = ()
def __init__(self, *a, **kw):
super(CrawlSpider, self).__init__(*a, **kw)
self._compile_rules() # . 调用重写父类的parse()函数来处理start_urls中返回的response对象
# . parse()则将这些response对象再次传递给了_parse_response()函数处理
# 2.1. _parse_response()函数中设置follow为True,该参数用于打开是否跟进链接提取
# . parse将返回item和跟进了的Request对象
def parse(self, response):
return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True) # 定义处理start_url中返回的response的函数,需要重写
def parse_start_url(self, response):
return [] # 结果过滤函数
def process_results(self, response, results):
return results # 从response中抽取符合任一用户定义'规则'的链接,并构造成Resquest对象返回
def _requests_to_follow(self, response):
if not isinstance(response, HtmlResponse):
return
seen = set() # 循环获取定义的url地址提取规则
for n, rule in enumerate(self._rules):
# 得到所有的提取规则列表
links = [l for l in rule.link_extractor.extract_links(response) if l not in seen]
# 使用用户指定的process_links处理每个连接
if links and rule.process_links:
links = rule.process_links(links)
#将链接加入seen集合,为每个链接生成Request对象,并设置回调函数为_repsonse_downloaded()
for link in links:
seen.add(link)
# 构造Request对象,并将Rule规则中定义的回调函数作为这个Request对象的回调函数
r = Request(url=link.url, callback=self._response_downloaded)
r.meta.update(rule=n, link_text=link.text)
# 对每个Request调用process_request()函数。该函数默认为indentify,即不做任何处理,直接返回该Request.
yield rule.process_request(r) # 采集数据链接处理,从符合规则的rule中提取链接并返回item和request
def _response_downloaded(self, response):
rule = self._rules[response.meta['rule']]
return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow) # 解析response对象,通过callback回调函数解析处理,并返回request或Item对象
def _parse_response(self, response, callback, cb_kwargs, follow=True):
# 首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是 parse_start_url函数)
#如果设置了回调函数(parse_start_url()),那么首先用parse_start_url()处理response对象,
# 然后再交给process_results处理。返回cb_res的一个列表
if callback:
#如果是parse调用的,则会解析成Request对象
#如果是rule callback,则会解析成Item
cb_res = callback(response, **cb_kwargs) or ()
cb_res = self.process_results(response, cb_res)
for requests_or_item in iterate_spider_output(cb_res):
yield requests_or_item # 如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象
if follow and self._follow_links:
#返回每个Request对象
for request_or_item in self._requests_to_follow(response):
yield request_or_item # 规则过滤
def _compile_rules(self):
def get_method(method):
if callable(method):
return method
elif isinstance(method, basestring):
return getattr(self, method, None) self._rules = [copy.copy(r) for r in self.rules]
for rule in self._rules:
rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request) # 链接跟踪全局配置设置
def set_crawler(self, crawler):
super(CrawlSpider, self).set_crawler(crawler)
self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)
(1) LinkExtractor链接提取对象
LinkExtract类型,主要目的是用于定义链接的提取匹配方式
该类中的方法extract_link()用于从响应对象response中提取符合定义规则的链接
该类型只会被实例化一次,但是在每次采集得到数据时重复调用
class scrapy.linkextractors.LinkExtractor(
allow = (), # 正则表达式,符合规则的链接会提取
deny = (), # 正则表达式,负责规则的链接会排除
allow_domains = (), # 允许的域名
deny_domains = (), # 禁止的域名
deny_extensions = None, # 是否允许扩展
restrict_xpaths = (), # xpath表达式,和allow配合使用精确提取数据
tags = ('a','area'), # 标签~
attrs = ('href'), # 指定提取的属性
canonicalize = True,
unique = True, # 唯一约束,是否去重
process_value = None
)
上述的参数中,我们可以看到通过一个linkextractors.LinkExtractor对象,可以定义各种提取规则,并且不需要考虑是否会将重复的链接添加到地址列表中
通过srapy shell做一个简单的测试,首先打开智联工作列表页面,终端命令行执行如下命令:
scrapy shell "http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%E5%8C%97%E4%BA%AC%2b%E4%B8%8A%E6%B5%B7%2b%E5%B9%BF%E5%B7%9E%2b%E6%B7%B1%E5%9C%B3&kw=python&isadv=0&sg=5b827b7808f548ad8261595837624f24&p=4"
此时scrapy就会自动从指定的地址中采集数据,并包含在response变量中,打开了python命令行,导入LinkExtractor类型并定义提取规则:
# 导入LinkExtractor类型
>>> from linkextractors import LinkExtractor
# 定义提取规则,包含指定字符的链接被提取
>>> links = LinkExtractor(allow=('7624f24&p=\d+'))
接下来,从响应数据中提取符合规则的超链接,执行extract_links()函数如下:
next_urls = links.extract_links(response)
打印next_urls,得到如下结果:
[Link(url='http://sou.zhaopin.com/jobs/searchresult.ashx
?jl=%E5%8C%%E4%BA%AC%2b%E4%B8%8A%E6%B5%B7%2b%E5%B9%BF%E
%B7%9E%2b%E6%B7%B1%E5%9C%B3&kw=python&isadv=&sg=5b827b7
808f548ad8261595837624f24&p=',text=u'\u767b\u5f55', frag
ment='', nofollow=True), Link(url='http://sou.zhaopin.com
/jobs/searchresult.ashx?j
l=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b%e5%b9%bf%e5%
b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=&sg=5b827b780
8f548ad8261595837624f24&p=', text=u'\u4e0a\u4e00\u9875',
fragment='', nofollow=False), Link(url='http://sou.zhaopi
n.com/jobs/searchresult.ashx?j
l=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b%e5%b9%bf%e5%b
%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=&sg=5b827b7808
f548ad8261595837624f24&p=', text='', fragment='', nofoll
ow=False), Link(url='http://sou.zhaopin.com/jobs/searchre
sult.ashx?jl=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b%e
%b9%bf%e5%b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=&s
g=5b827b7808f548ad8261595837624f24&p=', text='', fragme
nt='', nofollow=False), Link(url='http://sou.zhaopin.com/
jobs/searchresult.ashx?j
l=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b%e5%b9%bf%e5%
b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=&sg=5b827b780
8f548ad8261595837624f24&p=', text='', fragment='', nofo
llow=False), Link(url='http://sou.zhaopin.com/jobs/search
result.ashx?jl=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b
%e5%b9%bf%e5%b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=
&sg=5b827b7808f548ad8261595837624f24&p=', text='', frag
ment='', nofollow=False), Link(url='http://sou.zhaopin.co
m/jobs/searchresult.ashx?j
l=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b%e5%b9%bf%e5%
b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=&sg=5b827b780
8f548ad8261595837624f24&p=', text='', fragment='', nofo
llow=False), Link(url='http://sou.zhaopin.com/jobs/search
result.ashx?jl=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b
%e5%b9%bf%e5%b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=
&sg=5b827b7808f548ad8261595837624f24&p=', text='', frag
ment='', nofollow=False), Link(url='http://sou.zhaopin.co
m/jobs/searchresult.ashx?j
l=%e5%8c%%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b%e5%b9%bf%e5%
b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=&sg=5b827b780
8f548ad8261595837624f24&p=', text='...', fragment='', no
follow=False)]
我们可以很直观的看到,所有符合规则的连接全部被提取了出来
(2) Rule规则对象
Rule对象是链接操作规则对象,主要定义了对于LinkExtractor类型提取的超链接url地址的操作行为,可以在一个爬虫程序中定义多个Rule对象,包含在一个rules列表中即可
class scrapy.spiders.Rule(
# LinkExtractor对象
link_extractor,
# 回调函数,得到数据库之后调用的函数
callback = None,
# 回调函数调用时传递的参数列表
cb_kwargs = None,
# 是否从返回的响应数据中根据LinkExtractor继续提取,一般选择True
follow = None,
# 从LinkExtractor中提取的连接,会自动调用该选项指定的函数,用来进行超链接的筛选
process_links = None,
# 指定每个请求封装处理时要调用的函数
process_request = None
)
(3) 案例操作
智联招聘深度爬虫操作案例:
- 创建爬虫项目
scrapy startproject zhilianspider2
- 创建爬虫程序 在zhilianspider2/zhilianspider2/spiders/目录中创建zhilianspider.py文件,编辑如下:
# coding:utf- # 引入CrawlSpider, Rule, LinkExtractor模块
from scrapy.linkextractors import LinkExtractor
from scrapy.spider import CrawlSpider, Rule class ZhilianSpider(CrawlSpider):
"""
智联招聘深度爬虫处理类
继承scrapy.spiders.CrawlSpider类型
"""
# 定义爬虫名称
name = "cs2"
# 定义域名限制
allowed_domains = ["zhaopin.com"]
# 定义起始地址
start_urls = ("http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%E5%8C%97%E4%BA%AC%2b%E4%B8%8A%E6%B5%B7%2b%E5%B9%BF%E5%B7%9E%2b%E6%B7%B1%E5%9C%B3&kw=python&isadv=0&sg=5b827b7808f548ad8261595837624f24&p=1",) # 定义提取规则
links = LinkExtractor(
allow=("5837624f24&p=\d+")
) # 定义操作规则
rules = [
# 定义一个操作规则
Rule(links, follow=True, callback='parse_response'),
] # 定义数据处理函数
def parse_response(self, response):
# 提取数据
job_list = response.xpath("//div[@id='newlist_list_content_table']/table[@class='newlist'][position()>1]")
# 循环筛选数据
for job in job_list:
job_name = job.xpath("tr[1]/td[@class='zwmc']/div/a").xpath("string(.)").extract()[] print job_name print("*************************************************")
在终端命令行中执行如下命令运行爬虫程序
scrapy crawl cs2
可以在控制台看到具体的爬取信息,对于提取的数据全部进行了跟踪处理
..
[scrapy.core.engine] DEBUG: Crawled () <GET http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%e5%8c%97%e4%ba%ac%2
b%e4%b8%8a%e6%b5%b7%2b%e5%b9%bf%e5%b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python&isadv=&sg=5b827b7808f548ad8261595837624f24&p=> (referer: http
://sou.zhaopin.com/jobs/searchresult.ashx?jl=%e5%8c%97%e4%ba%ac%2b%e4%b8%8a%e6%b5%b7%2b%e5%b9%bf%e5%b7%9e%2b%e6%b7%b1%e5%9c%b3&kw=python
&isadv=&sg=5b827b7808f548ad8261595837624f24&p=) ....
图像算法工程师
软件测试工程师
********************************************************************
软件测试经理
高级软件测试工程师 ......
'scheduler/enqueued/memory': ,
'spider_exceptions/IOError': ,
'spider_exceptions/UnicodeEncodeError': ,
'start_time': datetime.datetime(, , , , , , )}
-- :: [scrapy.core.engine] INFO: Spider closed (shutdown)
scrapy初步解析源码即深度使用的更多相关文章
- Spark Streaming揭秘 Day5 初步贯通源码
Spark Streaming揭秘 Day5 初步贯通源码 引子 今天,让我们从Spark Streaming最重要的三个环节出发,让我们通过走读,逐步贯通源码,还记得Day1提到的三个谜团么,让我们 ...
- Flink 源码解析 —— 源码编译运行
更新一篇知识星球里面的源码分析文章,去年写的,周末自己录了个视频,大家看下效果好吗?如果好的话,后面补录发在知识星球里面的其他源码解析文章. 前言 之前自己本地 clone 了 Flink 的源码,编 ...
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- 转:[gevent源码分析] 深度分析gevent运行流程
[gevent源码分析] 深度分析gevent运行流程 http://blog.csdn.net/yueguanghaidao/article/details/24281751 一直对gevent运行 ...
- scrapy分布式Spider源码分析及实现过程
分布式框架scrapy_redis实现了一套完整的组件,其中也实现了spider,RedisSpider是在继承原scrapy的Spider的基础上略有改动,初始URL不在从start_urls列表中 ...
- mybatis源码级别深度剖析
mybatis 3.x源码深度解析与最佳实践 Mybatis源码解析优秀博文
- SPRING多个占位符配置文件解析源码研究--转
原文地址:http://www.cnphp6.com/archives/85639 Spring配置文件: <context:property-placeholder location=&quo ...
- FFmpeg中HLS文件解析源码
不少人都在找FFmpeg中是否有hls(m3u8)解析的源码,其实是有的.就是ffmpeg/libavformat/hlsproto.c,它依赖的文件也在那个目录中. 如果要是单纯想解析HLS的话,建 ...
- dubbo注册服务IP解析异常及IP解析源码分析
在使用dubbo注册服务时会遇到IP解析错误导致无法正常访问. 比如: 本机设置的IP为172.16.11.111, 但实际解析出来的是180.20.174.11 这样就导致这个Service永远也无 ...
随机推荐
- Yapi接口管理平台 本地部署 windows环境 -
YApi 是高效.易用.功能强大的 api 管理平台,旨在为开发.产品.测试人员提供更优雅的接口管理服务.可以帮助开发者轻松创建.发布.维护 API,YApi 还为用户提供了优秀的交互体验,开发人员只 ...
- Linux软件安装——安装软件的命令
Linux软件安装——安装软件的命令 摘要:本文主要学习了如何在Linux系统中安装.更新.卸载软件. rpm命令 rpm命令用来在Linux系统上进行软件的安装. 基本语法 安装命令: rpm -i ...
- Class文件和JVM的恩怨情仇
类的加载时机 现在我们例子中生成的两个.class文件都会直接被加载到JVM中吗?? 虚拟机规范则是严格规定了有且只有5种情况必须立即对类进行“初始化”(class文件加载到JVM中): 创建类的实例 ...
- SQL的概念与发展 - 极客时间学习笔记
了解SQL SQL的两个重要标准是SQL92和SQL99. SQL语言的划分 DDL,也叫Data Definition Language,也就是数据定义语言,用来定义数据库对象,包括数据库.数据表和 ...
- 4、Work-Queue
Work Queues using the Java Client In the first tutorial we wrote programs to send and receive messag ...
- [Python]使用生成器来简化代码
原本只是大概知道生成器是什么,但一直不知道怎么用,或是什么情景下用,后来才发现: 在需要一边读数据一边处理任务时,如果直接为每个任务都写一个函数,那么读数据的部分就要在每个函数都重复一遍 直接将所有任 ...
- router-link路由传参
router-link传参 [注意] 1.使用params方式传参时,只支持name跳转: 案例如下: 2.使用query传参,注意:两种都支持 案例如下 .
- 张兴盼-201871010131《面向对象程序设计(Java)》第七周学习总结
张兴盼-201871010131<面向对象程序设计(Java)>第七周学习总结 项目 内容 这个作业属于哪个课程 http://www.cnblogs.com/nwnu-daizh/ 这个 ...
- 代码审计-md5()函数
<?php error_reporting(0); $flag = 'flag{test}'; if (isset($_GET['username']) and isset($_GET['pas ...
- 使用Windows api 获得系统时间并生成文件夹
// 使用window api 获得系统时间 // 生成 #include "stdafx.h" #include <Windows.h> #include <d ...