今天我们就来介绍一下Scrapy框架提供的请求类和响应类,通过对源代码的深入分析,找出其共有的属性和方法以及一些使用技巧。这一节主要讲的是Scrapy框架中的基础知识,后面我们会经常用到这两个类。熟悉和掌握它们的源代码实现,对我们以后使用它们会有很大的帮助。
1. Request 类
首先,Scrapy中与请求相关的源代码位置如下:
scrapy中与请求相关的代码
可以看出,与请求定义相关的代码并不多,也方便我们学习和探索。让我们先看看请求类的定义:
#源代码位置:scrapy/http/request/_ _ init _ _。pyfromw3lib . URL import safe _ URL _ string #…class request(object _ ref):def _ _ init _(self,URL,callback = none,method = & # 39获取& # 39;,headers=None,body=None,cookies=None,meta=None,encoding = & # 39utf-8 & # 39;,priority=0,dont_filter=False,errback=None,flags=None,cb_kwargs=None): self。_encoding = encoding #这个必须先设置self.method = str(method)。上()自我。_set_url(url) self。_ set _ body(body)if not is instance(priority,int):raise type error(& # 34;请求优先级不是整数:% r & # 34% priority) self.priority =如果回调不是None且不可调用(callback)时的优先级:raise type error(& # 39;回调必须是可调用的,得到了% s & # 39% type(回调)。__name__)如果errback不是None且不可调用(errback):引发type error(& # 39;errback必须是可调用的,得到了% s & # 39% type(errback)。_ _ name _ _)self . callback = callback self . errback = errback self . cookies = cookies或{ } self . Headers = Headers(Headers or { },encoding = encoding)self . dont _ filter = dont _ filter self。_ meta = dict(meta)if meta else None self。_ CB _ kwargs = dict(CB _ kwargs)if CB _ kwargs else None self . flags =[]if flags is None else list(flags)# Code block 123456789101112141517181920212232425272930313233435从上面的源代码可以看出,Scrapy框架使用w3lib模块来完成一些web相关的功能,这里使用的是url模块safe_url_string()的方法是将url转换成合法的形式,即编码一些特殊的字符如中文、空等。请看下面的例子:
& gt& gt& gt从w3lib.url导入safe _ url _ strin & gt& gt& gturl = & # 34http://www.baidu.com/? XXX = zyz & # 34;& gt& gt& gtsafe _ URL _ string(URL)& # 39;http://www.baidu.com/? XXX = % 20 zyz & # 39;代码块1234中最终获得的URL形式与我们在浏览器中按Enter键时的形式相同。此外,在实例化请求类时,可以传入各种初始属性。常用属性具有以下含义:
url:请求地址;method:请求类型,GET|POST|PUT|DELETE 等;callback: HTTP 请求的回调方法,用于指定该 HTTP 请求的解析响应数据的方法;headers: 设置请求头。一般而言时设置请求头的 User-Agent 字段,模拟浏览器请求;body: 用于设置请求参数,比如登录请求需要带上用户名/密码等参数;cookies: 请求 cookies 信息,一般和登录认证相关,带上 cookies 用于表明身份信息。
熟悉了这个请求类之后,我们再来看一些在Request基础上进一步扩展的请求类。其中之一是FormRequest:
#来源位置:scrapy/http/request/form.py #…classform request(请求):valid _ form _ methods =[& # 39;获取& # 39;, '邮政& # 39;] def __init__(self,*args,* * kwargs):formdata = kwargs . pop(& # 39;表单数据& # 39;,None)如果formdata和kwargs . get(& # 39;方法& # 39;)是None:kwargs[& # 39;方法& # 39;] = '邮政& # 39;super(FormRequest,self)。__init__(*args,* * kwargs)if formdata:items = formdata . items()if is instance(formdata,dict)else formdata query str = _ urlencode(items,self . encoding)if self . method = = & # 39;邮政& # 39;:self . headers . set default(b & # 39;内容类型& # 39;,b & # 39application/x-www-form-urlencoded & # 39;)自我。_set_body(querystr) else: self。_ set _ URL(self . URL+(& # 39;&'如果& # 39;?'在self.url else & # 39?')+querystr) #…代码块12345678910112131415161718192021223 form request类主要用于提交表单请求,比如登录认证,比如提交订单等。它只支持GET和POST请求,与request类相比,FormRequest类多了一个表单参数属性,就是检查表单请求提交的数据。为了分析实例化时表单参数的处理,代码如下:
if formdata:items = formdata . items()if is instance(formdata,dict)else formdata query str = _ urlencode(items,self . encoding)if self . method = = & # 39;邮政& # 39;:self . headers . set default(b & # 39;内容类型& # 39;,b & # 39application/x-www-form-urlencoded & # 39;)自我。_set_body(querystr) else: self。_ set _ URL(self . URL+(& # 39;&'如果& # 39;?'在self.url else & # 39?')+ querystr) #…def _urlencode(seq,enc): values = [(to_bytes(k,enc),to_bytes(v,enc)) for k,VS in seq for V in(VS if is _ list like(VS)else[VS])]返回URL代码(values,Doseq = 1)代码块1234567891011213141516这段代码的逻辑非常清晰。如果有表单数据,它将被分为GET和POST请求:
GET 请求:将请求参数添加到 url 后面,用 “?” 连接,参数之间用 “&” 连接;POST 请求:一方面设置请求的 header,另一方面将数据放到 body 体中;
还有两个类,JsonRequest和XmlRpcRequest,它们使用不同的形式发送HTTP请求。让我们来看看这两个课程中的关键内容:
#来源位置:scrapy/http/request/JSON _ request . py #…class JSON request(request):def _ _ init _(self,* args,**kwargs): #…如果body_passed和data_passed: #…elif not body _ passed and data _ passed:kwargs[& # 39;正文& # 39;] =自我。_转储(数据)如果& # 39;方法& # 39;不在夸尔格斯:夸尔格斯[& # 39;方法& # 39;] = '邮政& # 39;超级(JsonRequest,self)。__init__(*args,* * kwargs)self . headers . set default(& # 39;内容类型& # 39;, '应用/JSON & # 39;)self . headers . set default(& # 39;接受& # 39;, 'application/json,text/jascript,*/*;q = 0.01 & # 39) # …在JsonRequest中,主要是将数据转换成json格式,然后保存在body属性中,再将请求头的Content-Type属性设置为“application/json”。
#源位置:scrapy/http/request/RPC . py import xmlrpc . clientas xmlrpc lib #…classxmlrpc请求(request): def _ _ init _ (self,* args,* * kwargs): #…如果& # 39;正文& # 39;不是在夸尔格和& # 39;params & # 39Inkwargs: kw = dict ((k,kwargs . pop(k))for k in dumps _ arg SIF k in kwargs)# Key places kwargs[& # 39;正文& # 39;] = xmlrpclib.dumps(**kw) #…代码块12345678910112131415 xmlrpcrrequest用于发送XML-RPC请求。重点是请求数据设置,使用了xmlrpc模块。
2. Respone 类
Response类主要封装了之前请求的响应结果,爬虫很重要的一部分就是解析这些响应,得到我们想要的结果。在这一部分,我们将深入分析响应类和扩展类。
响应类相关代码
查看源代码,我们可以获得以下信息:
init.py 中定义了 Response 基类;text.pym 中定义的 TextResponse 类直接继承 Response 类并进行了一系列扩展和重载部分方法;html.py 和 xml.py 中分别定义的 HtmlResponse 和 XmlResponse 都只继承了 TextResponse ,并未做过多的修改,只是分别取了个别名:# 源码位置:scrapy/http/response/html.py
from scrapy.http.response.text import TextResponse
class HtmlResponse(TextResponse):
pass
# 源码位置:scrapy/http/response/xml.py
from scrapy.http.response.text import TextResponse
class XmlResponse(TextResponse):
pass
接下来,我们的重点是学习Response类和TextResponse类。
响应类具有以下通用属性值:
headers:头部信息;status:返回状态码;body:响应内容;url:请求的 url;request:对应的 request 请求;ip_address:请求的 ip 地址。
我们还是通过Scrapy Shell请求广州链家二手房的地址,看看真实的回应,打印以上数值:
(scrapy-test)[root @ server ~]# scrapy shell https://gz.lianjia.com/ershoufang/-nolog[s]可用的Scrapy对象:[s] scrapy scrapy模块(包含Scrapy。请求,scrapy。选择器等)[s]爬虫& ltscrapy.crawler.Crawler对象位于0x7f9890d5a100 & gt项目{}[s]请求& lt得到https://gz.lianjia.com/ershoufang/>的回应& lt200 https://gz.lianjia.com/ershoufang/>[s]设置& ltscrapy.settings.Settings对象位于0x7f 9890d 57 bb 0 & gt;[s]蜘蛛& lt默认蜘蛛& # 39;默认& # 39;at 0x7f989034dd90 & gt[s]有用的快捷方式:[s] fetch(url[,redirect=True])获取url并更新本地对象(默认情况下,遵循重定向)[s] fetch(req)获取垃圾。请求和更新本地对象[s] shelp() Shell帮助(打印此帮助)[s]查看(响应)在浏览器中查看响应& gt& gt& gtresponse.headers { b & # 39服务器& # 39;:[b & # 39;贾立安& # 39;],b & # 39日期& # 39;:[b & # 39;2020年7月12日星期日07:37:16 GMT & # 39;],b & # 39内容类型& # 39;:[b & # 39;文本/html;charset = UTF-8 & # 39;],b & # 39变化& # 39;:[b & # 39;接受-编码& # 39;],b & # 39set-Cookie & # 39;:[b & # 39;select _ city = 440100expires = 2020年7月13日星期一07:37:16 GMT;max-Age = 86400;path =/;domain = .lianjia.com & # 39,b & # 39贾立安_ ssid = a 0980 b 19-93 F6-4942-a898-96ea 722d 524d;expires=Sun,2010年7月12日08:07:16 GMT;max-Age = 1800;domain = . Lian Jia . com;path =/& # 39;,b & # 39贾立安_ uuid = 12165 c9c-6c 66-4996-9e2c-623 a 838 EFD 4a;expires =周三,2010年7月10日07:37:16 GMT;max-Age = 315360000;domain = . Lian Jia . com;path =/& # 39;],b & # 39Via & # 39:[b & # 39;web 05-online . Zeus . LJ node . com & # 39;]} & gt;& gt& gtresponse.status200 & gt& gt& gtresponse.url & # 39https://gz.lianjia.com/ershoufang/' & gt;& gt& gtresponse . IP _ address IP v4 address(& # 39;211.159.232.241')& gt& gt& gt& gt& gt& gtresponse.request & lt得到https://gz.lianjia.com/ershoufang/> & gt;& gt注意:关于这个响应,我们在分析scrapy shell [url]命令的执行时说过,如果命令后面跟了要抓取的url地址,那么在交互shell生成之前,会把一些基本的环境变量包括请求URL的响应结果放入环境变量中,这也是为什么在这个交互模式下我们可以直接使用response来获取请求结果。
让我们来看看Response类中保留的一些方法:
#源位置:scrapy/http/response/_ _ init _。py #…class response(object _ ref):def _ _ init _(self,URL,status = 200,headers = none,body = b & # 39',flags=None,request=None,certificate=None,IP _ address = None):self . Headers = Headers(Headers or { })self . status = int(status)self。_set_body(身体)自我。_ set _ URL(URL)self . request = request self . flags =[]如果flags为None else list(flags)self . certificate = certificate self . IP _ address = IP _ address #…@ property def text(self):& # 34;""对于TextResponse的子类,这将把主体作为str & # 34""raise attribute error(& # 34;回复内容不是& # 39;t text & # 34)def css(self,*a,* * kw):& # 34;""仅由内容为文本的响应(TextResponse的子类)实现的快捷方法。"""提高不支持(& # 34;回复内容不是& # 39;t text & # 34)def xpath(self,*a,* * kw):& # 34;""仅由内容为文本的响应(TextResponse的子类)实现的快捷方法。"""提高不支持(& # 34;回复内容不是& # 39;t text & # 34) # …这些保留的文本属性、css()方法和xpath()方法都将在TextResponse中实现。接下来,我们仔细分析TextResponse的这些属性和方法:
# Source location:classtextresponse(response):_ default _ encoding = & # 39;ascii & # 39_ cached _ decoded _ JSON = _ NONE def _ _ init _ _(self,*args,**kwargs): self。_ encoding = kwargs . pop(& # 39;编码& # 39;,无)自我。_cached_benc =无自身。_cached_ubody =无自我。_ cached _ selector = None super(text response,self)。__init__(*args,**kwargs) #…从__init__()方法可以看出,TextResponse的属性和父类基本不变,只是增加了一些用于缓存的属性。接下来,让我们看看几个重要的属性和方法:
# …类TextResponse(响应):….@ property def text(self):& # 34;""unicode格式的正文& # 34;""#在_cached_ubody之前访问self.encoding以确保# _ body _ extruded _ encoding在self的情况下被调用benc = self.encoding。_cached_ubody为None:charset = & # 39;charset = % s & # 39% bencself。_ cached _ ubody = html _ to _ unicode(charset,self。正文)[1]回归自我。_ cached _ ubody #…上面代码的逻辑是将body属性中的值转换成str,我们可以在Scrapy Shell模式下重现这个操作:
(scrapy-test) [root@server ~]# scrapy shell https://www.baidu.com –nolog[s] ailable Scrapy objects:[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)[s] crawler <scrapy.crawler.Crawler object at 0x7faf4f318190>[s] item {}[s] request <GET https://www.baidu.com>[s] response <200 https://www.baidu.com>[s] settings <scrapy.settings.Settings object at 0x7faf4f315b50>[s] spider <DefaultSpider 'default' at 0x7faf4e9122b0>[s] Useful shortcuts:[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)[s] fetch(req) Fetch a scrapy.Request and update local objects [s] shelp() Shell help (print this help)[s] view(response) View response in a browser>>> response.bodyb'<!DOCTYPE html>\r\n<!–STATUS OK–><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>\xe7\x99\xbe\xe5\xba\xa6\xe4\xb8\x80\xe4\xb8\x8b\xef\xbc\x8c\xe4\xbd\xa0\xe5\xb0\xb1\xe7\x9f\xa5\xe9\x81\x93</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=\xe7\x99\xbe\xe5\xba\xa6\xe4\xb8\x80\xe4\xb8\x8b class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mn>\xe6\x96\xb0\xe9\x97\xbb</a> <a href=https://www.hao123.com name=tj_trhao123 class=mn>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mn>\xe5\x9c\xb0\xe5\x9b\xbe</a> <a href=http://v.baidu.com name=tj_trvideo class=mn>\xe8\xa7\x86\xe9\xa2\x91</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mn>\xe8\xb4\xb4\xe5\x90\xa7</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>\xe7\x99\xbb\xe5\xbd\x95</a> </noscript> <script>document.write(\'<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=\'+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ \'" name="tj_login" class="lb">\xe7\x99\xbb\xe5\xbd\x95</a>\');\r\n </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">\xe6\x9b\xb4\xe5\xa4\x9a\xe4\xba\xa7\xe5\x93\x81</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>\xe5\x85\xb3\xe4\xba\x8e\xe7\x99\xbe\xe5\xba\xa6</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>\xe4\xbd\xbf\xe7\x94\xa8\xe7\x99\xbe\xe5\xba\xa6\xe5\x89\x8d\xe5\xbf\x85\xe8\xaf\xbb</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>\xe6\x84\x8f\xe8\xa7\x81\xe5\x8f\x8d\xe9\xa6\x88</a> \xe4\xba\xacICP\xe8\xaf\x81030173\xe5\x8f\xb7 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>\r\n'>>> type(response.body)<class 'bytes'>>>> from w3lib.encoding import html_to_unicode>>> html_to_unicode("charset=None", response.body)('utf-8', '<!DOCTYPE html>\r\n<!–STATUS OK–><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mn>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mn>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mn>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mn>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mn>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write(\'<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=\'+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ \'" name="tj_login" class="lb">登录</a>\');\r\n </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>\r\n')
可以看到 ,Response 中的 body 属性值是 bytes 类型,通过 html_to_unicode() 方法可以将其转成 str,然后我们得到的网页文本就是 str 类型:(scrapy-test)[root @ server ~]# scrapy shell https://www.baidu.com-nolog[s]可用的Scrapy对象:[s] scrapy scrapy模块(包含Scrapy。请求,scrapy。选择器等)[s]爬虫& ltscrapy.crawler.Crawler对象位于0x 7 faf 4 f 318190 & gt;项目{}[s]请求& lt获取https://www . Baidu . com & gt;[s]响应& lt200 https://www . Baidu . com & gt;[s]设置& ltscrapy.settings.Settings对象位于0x 7 fa F4 f 315 b 50 & gt;[s]蜘蛛& lt默认蜘蛛& # 39;默认& # 39;at 0x 7 fa F4 e 9122 b 0 & gt;[s]有用的快捷方式:[s] fetch(url[,redirect=True])获取url并更新本地对象(默认情况下,遵循重定向)[s] fetch(req)获取碎片。请求和更新本地对象[s] shelp() Shell帮助(打印此帮助)[s]查看(响应)在浏览器中查看响应& gt& gt& gtresponse.bodyb & # 39& lt!DOCTYPE html & gt\ r \ n & lt!-状态正常-& gt;& lthtml & gt& lthead & gt& ltmeta http-equiv = content-type content = text/html;charset = utf-8 & gt;& ltmeta http-equiv=X-UA兼容的内容= IE = Edge & gt& ltmeta content = always name = referrer & gt;& ltlink rel = style sheet type = text/CSS href = https://ss1 . BD static . com/5en 1 bjq 8 aauym 2 zgoy 3k/r/www/cache/bdorz/Baidu . min . CSS & gt;& lttitle & gt\ xe7 \ x99 \ xbe \ xe5 \ xba \ xa6 \ xe4 \ xb8 \ X80 \ xe4 \ xb8 \ x8b \ xef \ xbc \ x8c \ xe4 \ xbd \ xa0 \ xe5 \ xb0 \ xb1 \ xe7 \ x9f \ xa5 \ xe9 \ x81 \ x93 & lt;/title & gt;& lt/head & gt;& ltbody link=#0000cc>。& ltdiv id = wrapper & gt& ltdiv id = head & gt& ltdiv class = head _ wrapper & gt& ltdiv class = s _ form & gt& ltdiv class = s _ form _ wrapper & gt& ltdiv id = lg & gt& ltimg hide focus = true src =//www . Baidu . com/img/BD _ logo 1 . png width = 270 height = 129 & gt;& lt/div & gt;& ltform id = form name = f action =//www . Baidu . com/s class = FM & gt;& ltinput type = hidden name = bdorz _ come value = 1 & gt;& lt输入类型=隐藏名称=ie值= utf-8 & gt;& lt输入类型=隐藏名称=f值= 8 & gt& lt输入类型=隐藏名称=rsv_bp值= 1 & gt& lt输入类型=隐藏名称=rsv_idx值= 1 & gt& lt输入类型=隐藏名称=tn值=百度& gt& ltspan class = & # 34bg s _ ipt _ wr & # 34& gt& ltinput id = kw name = wd class = s _ ipt value maxlength = 255 autocomplete = off auto focus = auto focus & gt;& lt/span>。& ltspan class = & # 34bg s _ btn _ wr & # 34& gt& ltinput type = submit id = su value = \ xe7 \ x99 \ xbe \ xe5 \ xba \ xa6 \ xe4 \ xb8 \ X80 \ xe4 \ xb8 \ x8b class = & # 34;bg s _ btn & # 34自动对焦& gt& lt/span>。& lt/form & gt;& lt/div & gt;& lt/div & gt;& ltdiv id = u1 & gt& lta href = http://news . Baidu . com name = TJ _ tr news class = mn & gt;\ xe6 \ x96 \ xb0 \ xe9 \ x97 \ xbb & lt/a & gt;& lta href = https://www . Hao 123 . com name = TJ _ tr Hao 123 class = mn & gt;hao123 & lt/a & gt;& lta href = http://map . Baidu . com name = TJ _ tr map class = mn & gt;\ xe5 \ x9c \ xb0 \ xe5 \ x9b \ xbe & lt/a & gt;& lta href = http://v . Baidu . com name = TJ _ tr video class = mn & gt;\ xe8 \ xa7 \ x86 \ xe9 \ xa2 \ x91 & lt/a & gt;& lta href = http://tie ba . Baidu . com name = TJ _ trtieba class = mn & gt;\ xe8 \ xb4 \ xb4 \ xe5 \ x90 \ xa7 & lt/a & gt;& ltnoscript & gt& lta href = http://www . Baidu . com/bdorz/log in . gif?log in & TPL = Mn & u = http % 3A % 2F % 2fwww . Baidu . com % 2f % 3fbdorz _ come % 3d 1 name = TJ _ log in class = lb & gt;\ xe7 \ x99 \ xbb \ xe5 \ xbd \ x95 & lt/a & gt;& lt/noscript & gt;& lt脚本& gtdocument . write(\ & # 39;& lta href = & # 34http://www.baidu.com/bdorz/login.gif?登录& tpl = mn & u = \ & # 39+encodeURIComponent(window . location . href+(window . location . search = = = & # 34;"?"?": "&")+ "bdorz _ come = 1 & # 34)+ \'"name = & # 34tj _ login & # 34class = & # 34lb & # 34& gt\ xe7 \ x99 \ xbb \ xe5 \ xbd \ x95 & lt/a & gt;\');\ r \ n & lt/script & gt;& lta href =//www . Baidu . com/more/name = TJ _ briicon class = bri style = & # 34;显示:块;"& gt\ xe6 \ x9b \ xb4 \ xe5 \ xa4 \ x9a \ xe4 \ xba \ xa7 \ xe5 \ x93 \ x81 & lt;/a & gt;& lt/div & gt;& lt/div & gt;& lt/div & gt;& ltdiv id = ftCon & gt& ltdiv id = ftConw & gt& ltp id = lh & gt& lta href = http://home . Baidu . com & gt;\ xe5 \ x85 \ xb3 \ xe4 \ xba \ x8e \ xe7 \ x99 \ xbe \ xe5 \ xba \ xa6 & lt;/a & gt;& lta href = http://IR . Baidu . com & gt;关于百度& lt/a & gt;& lt/p & gt;& ltp id = cp & gt2017百度& lta href = http://www . Baidu . com/duty/& gt;\ xe4 \ xbd \ xbf \ xe7 \ x94 \ xa8 \ xe7 \ x99 \ xbe \ xe5 \ xba \ xa6 \ xe5 \ x89 \ x8d \ xe5 \ xbf \ x85 \ xe8 \ xaf \ xbb & lt;/a & gt;& lta href = http://Jianyi . Baidu . com/class = CP-feedback & gt;\ xe6 \ x84 \ x8f \ xe8 \ xa7 \ x81 \ xe5 \ x8f \ x8d \ xe9 \ xa6 \ x88 & lt;/a & gt;\ xe4 \ xba \ xac ICP \ xe8 \ xaf \ x 81030173 \ xe5 \ x8f \ xb7 & lt;img src =//www . Baidu . com/img/GS . gif & gt;& lt/p & gt;& lt/div & gt;& lt/div & gt;& lt/div & gt;& lt/body & gt;& lt/html & gt;\ r \ n & # 39& gt& gt& gttype(response . body)& lt;班& # 39;字节& # 39;& gt& gt& gt& gt从w3lib.encoding导入html _ to _ unicode & gt& gt& gthtml _ to _ unicode(& # 34;charset = None & # 34,response . body)(& # 39;utf-8 & # 39;, '& lt!DOCTYPE html & gt\ r \ n & lt!-状态正常-& gt;& lthtml & gt& lthead & gt& ltmeta http-equiv = content-type content = text/html;charset = utf-8 & gt;& ltmeta http-equiv=X-UA兼容的内容= IE = Edge & gt& ltmeta content = always name = referrer & gt;& ltlink rel = style sheet type = text/CSS href = https://ss1 . BD static . com/5en 1 bjq 8 aauym 2 zgoy 3k/r/www/cache/bdorz/Baidu . min . CSS & gt;& lttitle & gt百度一下,你就知道了
& gt& gt& gttext = html _ to _ unicode(& # 34;charset = None & # 34,response . body)& gt;& gt& gttype(text[1])& lt;班& # 39;str & # 39& gt我们在上一节介绍了以下三种方法。正是因为有了这些属性和方法,我们才可以使用response.xpath()或者response.css()的编写方法来提取网页数据。
# …类TextResponse(响应):….@ property def Selector(self):from scrapy . Selector如果self则导入选择器。_cached_selector为None: self。_cached_selector = Selector(self)返回self。_cached_selector def xpath(self,query,**kwargs):返回self.selector.xpath(query,**kwargs) def css(self,query):返回self。selector.css (query)这些是textresponse类比较重要的属性和方法,其他的需要深入研究相关的方法及其功能。现在让我们来回答上一节中提出的问题:
为什么Scrapy的TextResponse实例可以使用这样的表达式:response.xpath(…).extract () [0]或response.xpath(…).extract _ first()?
接下来,我们带着这个问题继续追溯代码。让我们以上一节中的例子为例,打印response.xpath()的返回类型:
(scrapy-test)[root @ server ~]# scrapy shell https://gz.lianjia.com/ershoufang/-nolog…& gt& gt& gtdata = response . XPath(& # 39;//ul[@ class = & # 34;sellListContent & # 34]/Li/div/div[@ class = & # 34;标题& # 34;]/a/text()& # 39;)& gt& gt& gt类型(数据)& lt班& # 39;scrapy . selector . unified . selector list & # 39;& gt& gt& gt& gt如您所见,结果是一个SelectorList实例。让我们看看相应的已定义代码:
#源代码位置:scrapy/Selector/unified . py from Parsel导入选择器As _ Parselselector #…类选择器列表(_ Parselselector。选择器列表_ CLS,object _ ref):& # 34;"":class:`SelectorList ‘类是builtin “list ` `类的子类,它提供了一些附加的方法。"""它直接继承了parsel模块中的selectorlist_cls。继续看这个值的定义:
#源位置:Parsel/selector.py类选择器(对象):#…选择列表_ CLS =选择列表#…代码块12345678#源位置:Parsel/selector.py类选择器列表(list): #…def get all(self):& # 34;""为此列表中的每个元素调用“ . get()“方法,并以unicode字符串列表的形式返回展平后的结果。"""return[x . get()for x in self]extract = get all def get(self,default = None):& # 34;""返回列表中第一个元素的结果。如果列表为空,则返回默认值。"""对于x in self:return x . get()return default extract_first = get找到extract()和extract _ first()方法了吗?注意理解这段代码:
对于x in self:return x . get()return default self表示SelectorList的一个实例,实际上是一个列表,列表中的元素是Selector的实例。这个for循环相当于取一个元素,然后直接返回。返回值是x.get(),这里会涉及到Selector类的get()方法:
#源位置:parsel/selector . py from lxml import tree,html #…classselector(对象):#…def get(self):& # 34;""在单个unicode字符串中序列化并返回匹配的节点。编码内容的百分比未被引用。"""try:返回etree.tostring(self.root,method=self。_tostring_method,encoding = & # 39unicode & # 39,with _ tail = False)except(attribute error,TypeError):如果self.root为True:返回u & # 391'elif self.root为False:return u & # 39;0'else:return six . text _ type(self . root)#…我们可以继续在Scrapy Shell中做一个测试:
& gt& gt& gtdata _ list = response . XPath(& # 39;//ul[@ class = & # 34;sellListContent & # 34]/Li/div/div[@ class = & # 34;标题& # 34;]/a/text()& # 39;)& gt& gt& gttype(data _ list)& lt;班& # 39;scrapy . selector . unified . selector list & # 39;& gt& gt& gt& gtdata = data _ list[0]& gt;& gt& gt类型(数据)& lt班& # 39;scrapy . selector . unified . selector & # 39;& gt& gt& gt& gtdata . get()& # 39;地铁口总价低,精装实用小两室& # 39;选择器的get()方法最终提取我们匹配的文本,因此SelectorList中的extract()[0]和extract_first()方法将获得相同的结果:
& gt& gt& gtresponse . XPath(& # 39;//ul[@ class = & # 34;sellListContent & # 34]/Li/div/div[@ class = & # 34;标题& # 34;]/a/text()& # 39;).extract _ first()& # 39;地铁口总价低,精装实用小两室& # 39;& gt& gt& gtresponse . XPath(& # 39;//ul[@ class = & # 34;sellListContent & # 34]/Li/div/div[@ class = & # 34;标题& # 34;]/a/text()& # 39;).extract()[0]& # 39;地铁口总价低,精装实用小两室& # 39;有了这种一步一步的跟踪和实验,源代码中的很多语句都会清晰,我们在使用请求类和响应类时也会更加得心应手。请求的实例化需要哪些参数,响应的实例化有哪些方法,这些疑惑都会在源代码面前得到解答。
3. 小结
本节主要深入Scrapy的源代码,了解框架中定义的请求和响应类。通过源代码,我们可以了解它的可用属性和方法,只有这样,我们才能很好地掌握和使用Scrapy框架。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。