资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,2018/11/15,#,Python Crawler Development,极客学院,J,互联网,+,职业技能系列,Python,爬虫开发 从入门到实战(微课版),人民邮电出版社,谢乾坤著,第,7,章,异步,加载与请求头,如果,读者在本世纪初就接触过互联网,那么应该会记得,那个时候每单击一个链接,浏览器就会短暂地“白屏”一两秒,然后才会进入一个新的页面。不同的页面,网址也是不一样的。,随着,技术的不断进步,现在不少网站已经引入了异步加载技术,单击新的链接以后,几乎看不到“白屏”的现象了。而且更神奇的是,单击了链接,网页的内容已经发生了改变,但是网址竟然没有变,。,通过,这一章的学习,你将会掌握如下知识。,(,1,)抓取异步加载的数据。,(,2,)伪造,HTTP,请求头。,(,3,)模拟浏览器获取网站数据。,7,.1,异步加载,7.1.1 AJAX,技术介绍,AJAX,是,Asynchronous JavaScript And XML,的首字母缩写,意为异步,JavaScript,与,XML,。,使用,AJAX,技术,可以在不刷新网页的情况下更新网页数据。使用,AJAX,技术的网页,一般会使用,HTML,编写网页的框架。,在打开网页的时候,首先加载的是这个框架。剩下的部分将会在框架加载完成以后再通过,JavaScript,从后台加载。,如何,判断一个网页有没有使用,AJAX,技术呢?请访问,http,:/exercise.kingname.info/exercise_ajax_1.html,,这个页面用浏览器访问的结果如图,7,-,1,所示。,但是,如果检查它的源代码,会发现源代码里面并没有网页上面显示的这两段文字,如图,7,-,2,所示。,图,7-1,异步加载,练习页面,1,图,7-2,使用异步加载技术的网页,数据不在源代码中,7.1.2 JSON,介绍与应用,JSON,的全称是,JavaScript Object Notation,,是一种轻量级的数据交换格式。网络之间使用,HTTP,方式传递数据的时候,绝大多数情况下传递的都是字符串。,因此,当需要把,Python,里面的数据发送给网页或者其他编程语言的时候,可以先将,Python,的数据转化为,JSON,格式的字符串,然后将字符串传递给其他语言,其他语言再将,JSON,格式的字符串转换为它自己的数据格式。,为了,直观地观察一个,JSON,格式的字符串,先在,Python,中初始化一个字典:,person=,basic_info:name:kingname,age:24,sex:male,merry:False,work_info:salary:99999,position:engineer,department:None,不仅,是字典,,Python,中的列表或者包含字典的列表,也可以转换为,JSON,格式的字符串,如图,7-3,所示。,如果,要把,JSON,格式的字符串转换为,Python,的字典或者列表,只需要使用一行代码即可:,person_dict=json.loads(person_json_indent),这里,得到的,person_dict,就是一个字典,可以像使用普通字典一样来使用它,如图,7-4,所示。,图,7-3,将包含字典的列表转换为,JSON,格式的字符串,图,7-4,把,JSON,格式的,字符串转换为字典,7.1.3,异步,GET,与,POST,请求,使用,异步加载技术的网站,被加载的内容是不能在源代码中找到的。对于这种情况,应该如何抓取被加载的内容呢?,为了,解决这个问题,就需要使用,Google Chrome,浏览器的开发者模式。在网页上单击右键,选择“检查”命令,然后定位到“,Network,”选项卡,如图,7-5,所示,。,图,7-5,打开,Google Chrome,开发者工具,接下来,需要刷新网页。在,Windows,下,按,F5,键或者单击地址栏左边的“刷新”按钮,在,Mac OS,下,按,Shift+Command+R,组合键或者单击地址栏左边的“刷新”按钮。刷新以后,可以看到“,Network,”选项卡下面出现了一些内容,如图,7-6,所示。,图,7-6,刷新网页以后,“,Network,”选项卡下出现的内容,单击,“,Network,”选项卡下面出现的“,ajax_1_backend,”和“,ajax_1_postbackend,”,并定位到“,Response,”选项卡,可以看到这里出现了网页上面的内容,如图,7-7,和图,7-8,所示。,图,7-7,被异步加载的数据之一,使用,GET,方式,图,7-8,被异步加载的数据之二,使用,POST,方式,再,选择“,Headers,”选项卡,可以看到这个请求使用,GET,方式,发送到,exercise.kingname.info/ajax_1_ backend,,其头部信息如图,7-9,所示。,于是,,尝试使用,requests,发送这个请求,即可成功获取到网页上的第,1,条内容,如图,7-10,所示。,图,7-9,使用,GET,方式的异步请求的头部信息,图,7-10,使用,requests,获得被异步加载的信息,对于,网页中的第,2,条内容,查看“,Headers,”选项卡,可以看到,这是使用,POST,方式向,,exercise.kingname.info/ajax_1_postbackend,发送请求,并以,JSON,格式提交数据,其头部信息如图,7-11,所示。,使用,requests,发送这个请求,也成功地获取了网页上面的第,2,条信息。通过修改请求的数据内容,还能够修改网页的返回内容,如图,7-12,所示。,图,7-11,使用,POST,方式的异步请求的头部,信息,图,7-12,使用,requests,模拟发送,POST,请求获取第,2,条异步加载内容,7.1.4,特殊的异步加载,7.1.3,小节中介绍的是最常见、最简单的异步加载情况,但并非所有的异步加载都会向后台发送请求。打开,AJAX,的第,2,个练习页面,可以看到页面上有图,7,-,13,所示的内容。,图,7-13,异步加载练习页面,2,分析,Chrome,开发者工具的“,Network”,选项卡下面的内容,可以看到整个页面的打开过程并没有尝试请求后台的行为。其中的,exercise_ajax_2.html,就是这个页面自身,而,jquery-3.2.1.min.js,是,jQuery,的库,都不是对后台的请求。打开网页源代码可以看到,确实没有“天王盖地虎”这几个汉字,如图,7,-,14,所示。,图,7-14,网页源代码中确实没有网页中的内容,那么这个页面上的汉字到底是从哪里加载进来的?这种情况称为伪装成异步加载的后端渲染。数据就在源代码里,但却不直接显示出来。注意,源代码最下面的,JavaScript,代码,其中有一段:,code:u884cu52a8u4ee3u53f7uff1au5929u738bu76d6u5730u864e,其外形看起来有点像,JSON,格式的字符串。尝试使用,Python,去解析,发现可以得到网页上面的内容,如图,7,-,15,所示。,图,7-15,解析,JSON,字符串得到网页上显示的内容,这种,假的异步加载页面,其处理思路一般是使用正则表达式从页面中把数据提取出来,然后直接解析。对于异步加载练习页面,2,,完整的处理代码为:,import json,import requests,import re,url=exercise.kingname.info/exercise_ajax_2.html,html=requests.get(url).content.decode(),code_json,=re.search(secret=(.*?),html,re.S).group(1),code_dict=json.loads(code_json),print(code_dictcode),运行,后的结果如图,7-16,所示。,图,7-16,获取假异步加载的数据,7.1.5,多次请求的异步加载,图,7-17,异步加载练习页面,3,还有一些网页,显示在页面上的内容要经过多次异步请求才能得到。第,1,个,AJAX,请求返回的是第,2,个请求的参数,第,2,个请求的返回内容又是第,3,个请求的参数,只有得到了上一个请求里面的有用信息,才能发起下一个请求。,打开异步加载练习页,3,,页面内容如图,7,-,17,所示。,通过,分析,Chrome,开发者工具的请求,不难发现这一条信息是通过向,,exercise.kingname.info/ajax_3_postbackend,这个地址发送,POST,请求得到的,如图,7-18,所示。,图,7-18,通过,Chrome,开发者工具找到页面信息的来源,其中,,返回的,JSON,格式的字符串经过,Python,解析,可以得到页面上的文字,如图,7-19,所示。,图,7-19,使用,Python,解析发现请求返回的内容确实是页面内容,在,“,Headers,”选项卡查看这个,POST,请求的具体参数,在,body,里面发现两个奇怪的参数,secret1,和,secret2,,如图,7-20,所示。,图,7-20,分析请求的,body,信息发现两,个,奇怪,参数,secret1,和,secret2,到目前为止,一切看起来都和,7.1.3,小节中的,POST,请求一样。但是在,7.1.3,小节里面提交的参数是可以随便修改的,那么在这里如果随便修改会怎么样呢?尝试修改,secret1,和,secret2,,发现,POST,请求无法得到想要的结果,如图,7,-,21,所示。,图,7-21,修改,secret1,或者,secret2,发现不能得到想要的结果,打开,这个练习页的源代码,在源代码中可以找到,secret_2,,如图,7-22,所示。虽然在,POST,参数中,名字是,secret2,,而源代码中的名字是,secret_2,,不过从值可以看出这就是同一个参数。,图,7-22,在源代码中找到,secret_2,源代码,里面没有,secret1,,因此就要考虑这个参数是不是来自于另一个异步请求,。,继续,在开发者工具中查看其他请求,可以成功找到,secret1,,如图,7-23,所示。注意,它的名字变为了“,code,”,但是从值可以看出这就是,secret1,。不少网站也会使用这种改名字的方式来迷惑爬虫开发者。,图,7-23,在另一个异步请求里面发现了,secret1,这,一条请求就是一个不带任何参数的,GET,请求,请求的头部信息如图,7-24,所示。,对于,这种多次请求才能得到数据的情况,解决办法就是逐一请求,得到返回结果以后再发起下一个请求。具体到这个例子中,那就是先从源代码里面获得,secret2,,再通过,GET,请求得到,secret1,,最后使用,secret1,和,secret2,来获取页面上显示的内容。,使用,Python,来实现这个过程,代码和运行结果如图,7-25,所示。,图,7-24,获得,secret1,的请求的头部信息,图,7-25,使用,Python,模拟多次异步请求并获得页面上的值,7.1.6,基于异步加载的简单登录,网站的登录方式有很多种,其中有一种比较简单的方式,就是使用,AJAX,发送请求来进行登录。请打开,AJAX,第,4,个练习页,exercise.kingname.info/exercise_ajax_4.html,,这个页面实现了简单的登录功能。页面打开以后的效果如图,7,-,26,所示。,根据输入框中的提示,使用用户名“,kingname”,和密码“,genius”,进行登录,可以看到登录成功以后弹出图,7,-,27,所示的提示框。,图,7-26,使用异步加载实现的登录页面,图,7-27,登录成功后弹出的提示框,对于,这种简单的登录功能,可以使用抓取异步加载网页的方式来进行处理。在,Chrome,开发者工具中可以发现,当单击“登录”按钮时,网页向后台发送了一条请求,如图,7-28,所示。,图,7-28,登录过程实际上是一个异步的请求,这,条请求返回的内容就是“通关口令”。再来看看这个请求发送了哪些数据,如图,7-29,所示。,图,7-29,登录请求发送的数据,这,就是使用,POST,方式的最简单的,AJAX,请求。使用获取,POST,方式的,AJAX,请求的代码,就能成功获取,到,登录以后返回的内容,如图,7-30,所示。,图,7-30,使用,AJAX,请求获得登录返回的内容,7.2,请求头(,Headers,),7.2.1,请求头的作用,使用计算机网页版外卖网站的读者应该会发现这样一个现象:第一次登录外卖网页的时候会让你选择当前所在的商业圈,一旦选定好之后关闭浏览器再打开,网页就会自动定位到先前选择的商业圈。,又比如,例如携程的网站,使用计算机浏览器打开的时候,页面看起来非常复杂多样,如图,7,-,31,所示。,但同一个网址,使用手机浏览器打开时,网址会自动发生改变,而且得到的页面竟然完全不同,如图,7,-,32,所示。,图,7-31,计算机网页版携程首页,图,7-32,手机版携程首页,网站,怎么知道现在是计算机浏览器还是手机浏览器在访问这个页面呢?网站怎么能记住地理位置呢?这就要归功于,Headers,了,。,Headers,称为请求头,浏览器可以将一些信息通过,Headers,传递给服务器,服务器也可以将一些信息通过,Headers,传递给浏览器。电商网站常常应用的,Cookies,就是,Headers,里面的一个部分。,7.2.2,伪造请求头,打开练习页,exercise.kingname.info/exercise_headers.html,,使用,Chrome,的开发者工具监控这个页面的网页请求,可以看到图,7,-,33,所示的内容。,图,7-33,请求头练习页,页面,看起来像是发起了一个普通的,GET,方式的异步请求给,exercise.kingname.info/exercise_headers_backend,。使用,requests,尝试获取这个网址的返回信息,结果如图,7-34,所示。,图,7-34,使用,requests,访问请求头练习页面失败,使用,浏览器访问网站的时候,网站可以看到一个名称为,Headers,(请求头)的东西,它的内容如图,7-35,所示。,图,7-35,使用浏览器访问网站后台显示的,Headers,信息,如果,使用,requests,访问,请求头的内容如图,7-36,所示。,图,7-36,使用,requests,访问网站,后台显示的,Headers,信息,为了,解决这个问题,就需要给爬虫“换头”。把浏览器的头安装到爬虫的身上,这样网站就不知道谁是谁了。要换头,首先就需要知道浏览器的头是什么样的。因此需要在,Chrome,浏览器开发者工具的“,Network,”选项卡的,Request Headers,里面观察这一次请求的请求头,如图,7-37,所示。,图,7-37,浏览器发起的请求的头部信息,在,requests,里面,设置请求头的参数名称为“,headers,”,它的值是一个字典。带有请求头的请求,使用,requests,的发送格式为:,html=requests.get(url,headers=,字典,).content.decode(),html=requests.post(url,json=xxx,headers=,字典,).content.decode(,),代码,中的字典就对应了浏览器中的请求头。在爬虫里面创建一个字典,将,Chrome,的请求头的内容复制进去,并调整好格式,发起一个带有,Chrome,请求头的爬虫请求,可以发现请求获得成功,如图,7-38,所示。,图,7-38,更换了,Chrome,头部以后爬虫访问成功,虽然,对于某些网站,在请求头里面只需要设置,User-Agent,就可以正常访问了,但是为了保险起见,还是建议把所有项目都带上,这样可以让爬虫更“像”浏览器。例如本练习,如果仅仅设置,User-Agent,的话,会得到图,7-39,所示的返回信息。,图,7-39,仅仅修改,User-Agent,是不能骗过练习网站的,7.3,模拟浏览器,有一些,网站在发起,AJAX,请求的时候,会带上特殊的字符串用于身份验证。这种字符串称为,Token,。为了简单起见,请打开练习页面,这个页面在发起,AJAX,请求的时候会在,Headers,中带上一个参数,ReqTime,;在,POST,发送的数据中会有一个参数,sum,,如图,7-40,所示。,图,7-40,较为复杂的异步加载练习页面,多次,刷新页面,可以发现,ReqTime,和,sum,一直在变化。如果,requests,只固定使用某个,ReqTime,与,sum,的组合来发起请求,就会出现图,7-41,所示的返回信息。,不难,看出,ReqTime,是精确到毫秒的时间戳,即使使用,Python,生成了一个时间戳,也不能得到网页上面的内容,如图,7-42,所示。,图,7-41,如果使用固定的参数就会导致爬虫爬不到数据,图,7-42,仅仅修改时间戳是不能让爬虫成功的,7.3.1 Selenium,介绍,虽然在网页的源代码中无法看到被异步加载的内容,但是在,Chrome,的开发者工具的“,Elements”,选项卡下却可以看到网页上的内容,如图,7,-,43,所示。,图,7-43,在开发者工具的“,Elements,”选项卡,下,可以,看到被加载的内容,7.3.2 Selenium,安装,使用,pip,安装,Selenium,:,pip install selenium,安装情况如图,7,-,44,所示。,图,7-44,安装,Selenium,下载,ChromeDriver,,根据自己的系统选择合适的版本,如图,7-45,所示。,图,7-45,根据自己的系统选择合适的版本,7.3.3 Selenium,的使用,1,获取源代码,将,chromedriver,与代码放在同一个文件夹中以方便代码直接调用。初始化,Selenium,只需要两行代码,导入,Selenium,库,再指定,WebDriver,,如图,7,-,46,所示。,图,7-46,初始化,Selenium,第,3,行代码指定了,Selenium,使用,ChromeDriver,来操作,Chrome,解析网页,括号里的参数就是,ChromeDriver,可执行文件的地址。,如果,要使用,PhantomJS,,只需要修改第,3,行代码即可:,driver=webdriver.PhantomJS(./phantomjs),同样,,需要将,PhantomJS,的可执行文件与代码放在一起。,需要,特别提醒的是,如果,chromedriver,与代码不在一起,可以通过绝对路径来指定,例如:,driver=webdriver.Chrome(/usr/bin/chromedriver),使用,Windows,的读者在写这个参数的时候,要注意反斜杠的问题。“,”这个符号叫作反斜杠,在,Windows,中作为路径的分隔符。但是由于转义字符也是反斜杠,所以如果把,Windows,下面的代码写为下面这样就会出问题。,driver=webdriver.Chrome(C:serverchromedriver.exe),因此,,使用,Windows,的读者可在路径字符串左引号的左边加一个“,r,”符号,将代码写为:,driver=webdriver.Chrome(rC:serverchromedriver.exe),这样,Python,就能正确处理反斜杠的问题。,初始化,完成以后,就可以使用,Selenium,打开网页了。要打开一个网页只需要一行代码:,driver.get(exercise.kingname.info/exercise_advanced_ajax.html),代码,运行以后会自动打开一个,Chrome,窗口,并在窗口里面自动进入这个网址对应的页面。一旦被异步加载的内容已经出现在了这个自动打开的,Chrome,窗口中,那么此时使用下列代码:,html=driver.page_source,就,能得到在,Chrome,开发者工具中出现的,HTML,代码,如图,7-47,所示,。,图,7-47,在,ChromeDriver,加载页面完成以后可以得到加载以后的源代码,2,等待信息出现,图,7-47,所示的代码第,6,行设置了一个,5s,的延迟,这是由于,Selenium,并不会等待网页加载完成再执行后面的代码。它只是向,ChromeDriver,发送了一个命令,让,ChromeDriver,打开某个网页,。,至于,网页要开多久,,Selenium,并不关心。由于被异步加载的内容会延迟出现,因此需要等待它出现以后再开始抓取。,3,在网页中获取元素,在,网页中寻找需要的内容,可以使用类似于,Beautiful Soup4,的语法:,element=driver.find_element_by_id(passwd-id)#,如果有多个符合条件的,返回第,1,个,element=driver.find_element_by_name(passwd)#,如果有多个符合条件的,返回第,1,个,element_list=driver.find_elements_by_id(passwd-id)#,以列表形式返回所有的符合条件的,element,element_list=driver.find_elements_by_name(passwd)#,以列表形式返回所有的符合条件的,element,也,可以使用,XPath,:,element=driver.find_element_by_xpath(/inputid=passwd-id),#如果有多个符合条件的,返回第1个,element=driver.find_elements_by_xpath(/divid=passwd-id),#,以列表形式返回所有的符合条件的,element,对于,练习网站,使用,XPath,获取网页的内容,运行结果如图,7-48,所示。,图,7-48,使用,Selenium,和,ChromeDriver,获得练习网站的内容,7.4,阶段案例,在乐视网上寻找一个视频,爬取视频的评论信息。,7.4.1,需求分析,目标网站:,。,目标内容:爬取视频评论。,涉及的知识点:,(,1,)分析网站的异步加载请求。,(,2,)使用,requests,发送请求。,7.4.2,核心代码构建,在乐视网上打开一个视频,可以看到其部会评论页面如图,7,-,49,所示。,图,7-49,乐视网部分视频评论页面,通过,使用,Chrome,的开发者工具分析页面的异步加载请求,可以发现评论所在的请求如图,7-50,所示。,可以,使用,Python,来模拟这个请求,从而获取视频的评论信息。,在,请求的,URL,里面有两个参数:,vid,和,pid,。这两个参数在网页的源代码里面都可以找到,如图,7-51,所示。,图,7-50,使用,Chrome,开发者工具观察评论的异步加载请求,图,7-51,在网页源代码里面寻找,pid,和,vid,爬虫,首先访问视频页面,通过正则表达式获取,vid,和,pid,,并将结果保存到“,necessary_info,”这个类属性对应的字典中。核心代码如下:,def get_necessary_id(self):,source=self.get_source(self.url,self.HEADERS),vid=re.search(vid:(d+),source).group(1),pid=re.search(pid:(d+),source).group(1),self.necessary_infoxid=vid,self.necessary_infopid=pid,访问,评论的接口,用,Python,发起请求,获得评论数据。核心代码如下:,def get_comment(self):,url=self.COMMENT_URL.format(xid=self.necessary_infoxid,pid=self.necessary_infopid),source=self.get_source(url,self.HEADERS),source_json=sourcesource.find():-1,comment_dict=json.loads(source_json),comments=comment_dictdata,for comment in comments:,print(f,发帖人:,commentuserusername,评论内容:,commentcontent),代码,中,提前定义的,self.COMMENT_URL,和,self.HEADERS,如图,7-52,所示。,图,7-52,在代码中提前定义好,self.COMMENT_URL,和,self.HEADERS,7.4.3,调试与运行,在爬虫中,带上通过,Chrome,浏览器从评论页面复制而来的,Headers,再发起请求,可以减少爬虫被网站封锁的概率。,爬虫的运行结果如图,7,-,53,所示。,图,7-53,乐视网视频评论爬虫运行结果,7.5,本章小结,本章,主要介绍了使用爬虫获取异步加载网页的各种方法。对于普通的异步加载,可以使用,requests,直接发送,AJAX,请求来获取被加载的内容,。,发送,的请求中可能包含一些特殊的值,这些值来自网页源代码或者另一个,AJAX,请求,。,在,发送请求时需要注意,应保持,requests,提交的请求头与浏览器的请求头一致,这样才能更好地骗过网站服务器达到获取数据的目的。,对于,比较复杂的异步加载,现阶段可以先使用,Selenium,和,ChromeDriver,来直接加载网页,然后就能从被加载的网页中直接获取到需要的内容。,7.6,动手实践,寻找,并爬取一个使用异步加载技术的网站。,
展开阅读全文