网络爬虫实践(二)-动态页面
来源:互联网 发布:shell编程 ${ 编辑:程序博客网 时间:2024/06/14 05:27
背景
我们可以采用查看网页源代码的方式,获取网页信息,但是,对于动态页面,很可能无法在源代码中,找到目标信息。比如,虾米精选集中,当精选集中的歌曲数目超过50首,点击加载更多后,直接查看网页源代码,依然无法看到第50首后的歌曲信息。
这是因为,使用了Ajax(Asynchronous JavaScript and XML)技术。在不重新加载整个页面的情况下,web与服务器实现数据交互。Ajax请求数据还是通过http协议传输,加载到的是json数据,然后在浏览器进行渲染。
实践
编程环境:python 2.7
爬取对象:虾米中,歌曲数超过50首的精选集(如果不超过50首,则无需加载新歌曲)
一、分析请求与响应
分析交互需借助工具,可使用fiddler等抓包工具,亦可使用Chrome自带的调试器。以下以Chrome调试器为工具。
触发页面加载更多数据是点击“点击加载更多”的时候。所以,先右键网页→审查元素→Network→清空。接着,点击”点击加载更多“。此时,出现“ajax-get-list”字样的GET链接,点击对应的text/html,则可以查看到请求和响应的相关信息。
查看浏览器发送的url,发现已不是原url。复制新url到浏览器搜索栏,得到json数据--这就是我们要找的东西!
{"state":0,"message":"","result":{"total_page":23,"data":[{"gmt_create":1431664142,"list_id":101007112,"song_id":1770385747,"description":"","status":0,"user_id":0,"thirdparty_flag":0,"is_check":0,"artist_name":"Wolfgang Amadeus Mozart","artist_id":103608,"name":"Wolfgang Amadeus Mozart: Symphony No.40 in G minor, K.550 - 1. Molto allegro","ordering":51},{"gmt_create":1431664142,"list_id":101007112,"song_id":1770421675,"description":"","status":0,"user_id":0,"thirdparty_flag":0,"is_check":0,"artist_name":"Murray Perahia","artist_id":61885,"name":"no.3 in e major","ordering":52},……]}}
继续分析:
第一次点击加载发送的url:
=1448693039684&id=101007112&p=2”>http://www.xiami.com/collect/ajax-get-list?=1448693039684&id=101007112&p=2
第二次点击加载发送的url:
=1448693039684&id=101007112&p=3”>http://www.xiami.com/collect/ajax-get-list?=1448693039684&id=101007112&p=3
……
规律分析看来,Ajax地址不同处在与“&p=”后的数字。
二、解析json数据
访问Ajax地址得到json数据,肉眼看上去是dict。试着打印key对应的value。
state = content['state']TypeError: string indices must be integers, not str
错误提示的意思是:字符的索引必须是整数。难道json数据不是”“肉眼看到的dict”?!尝试用type()看下类型。
结果显示,这个看起来很像dict的数据,确实是
三、保存文件
保存文件不难,分享下我遇到的写入文件过程中报错的问题。先简单引入:
运行:
fp.write(u'中')
结果:
UnicodeEncodeError: 'ascii' codec can't encode character u'\u4e2d' in position 0: ordinal not in range(128)
报错信息的意思是,ASCII无法对“中”进行编码。ASCII只能处理一个字节,而处理中文至少要2个字节,这个错误不难理解。关键是,我们从这个报错信息中,可以推测出, Python2.7基于ascii去处理字符流。而ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,只要将字符转换成UTF-8编码格式,就可以解决问题。即:
f.write((u'中').encode('utf-8'))
“先编码后写入”是一种解决方式。也可以,利用codecs()模块,先指明写入的数据流就是unicode string,如下:
import codecsf = codecs.open('filecode.txt','a','utf-8') f.write(u'中')
BTW,数据只能以字符形式写入,如果要写入数字,需先使用str()将数字转型。
感想
以前不理解,为什么有些岗位明明是要招黑盒测试工程师,但是要求有编程基础。也经常看到有些人吐槽说,做手工测试用不上编程……写这个代码,看到1000+多首歌瞬间就爬下来,突然就理解了。
假设现在测试工程师接到测试虾米歌曲列表这个模块的任务,“点击加载更多”这个基本功能是要测的,像这个有1110首歌的精选集,需点击button22次才能在页面显示完精选集内所有歌曲,要怎么测试?“点击一次两次button”这个功能就算完成测试,还是出于保守起见,“点击22次”,加载完所有歌曲才算测试完全?对于前者,我只问一句,如何保证服务器会正确返回后面的数据?对于后者,我夸奖下你的耐心,你是个用心工作的人,但请问,“点击22次”需要多长时间?耗时乘以送测版本数,等于“点点点”机械劳动的人力成本。对比:写个脚本,每个版本运行几秒,就可以知道服务器是否正确返回数据。是不是瞬间明白手工测试工程师会写代码的理由?!
附:代码
#! /usr/bin/env python#coding: utf-8import urllib2import jsonimport osimport redef visitServer(url): req = urllib2.Request(url) req.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36') try: respone = urllib2.urlopen(req) html = respone.read() respone.close() except URLError, e: print e.reason return htmlurl = "http://www.xiami.com/collect/101007112"html = visitServer(url)#一次加载最多显示几首歌rex1 = re.compile("trackid")limit = rex1.findall(html)#精选集的歌曲数rex = re.compile(r'<span>歌曲数:</span>(.+) ')songNO = rex.findall(html)[0]ajaxpageNO = int(songNO) / len(limit)if songNO % limit != 0: ajaxpageNO = ajaxpageNO + 1ajaxurl = "http://www.xiami.com/collect/ajax-get-list?_=1448693039684&id=101007112&p="if os.path.exists('loadmore.txt'): os.remove('loadmore.txt')fp = open ('loadmore.txt','a') for i in range(1,ajaxpageNO+1): url = ajaxurl + str(i) content = visitServer(url) data = json.loads(content) resultdict = data['result'] datalist = resultdict['data'] for i in range(len(datalist)): datadict = datalist[i] artistname = datadict['name'] artistid = datadict['ordering'] fp.write((unicode(artistid) + u'、' + unicode(artistname) + u'\n').encode('utf-8'))fp.close()
- 网络爬虫实践(二)-动态页面
- Python爬虫爬取动态页面思路+实例(二)
- 爬虫实践(二)--掌阅书城
- 【网络爬虫】【java】微博爬虫(二):如何抓取HTML页面及HttpClient使用
- Linux企业级项目实践之网络爬虫(17)——存储页面
- 网络爬虫基本原理(二)
- 网络爬虫基本原理(二)
- 网络爬虫基础(二)
- python网络爬虫(二)
- 网络爬虫学习(二)
- 网络爬虫规则(二)
- python网络爬虫(二)
- 网络爬虫中动态的获取页面编码
- Python爬虫实践笔记(二)
- 【爬虫学习4】Python爬取动态页面思路(二)
- 网络编程笔记二:一个java爬虫的实现(静态页面)
- Python 网络爬虫与信息获取(二)—— 页面内容提取
- Splash动态页面爬虫
- Window attributes属性详解[转载]
- CAT ----分布式实时监控系统
- (转载)数据库范式及宽表窄表理解
- .DRPM File Extension
- 【数据结构】二叉树的原理及实现学习总结
- 网络爬虫实践(二)-动态页面
- poj 2420 A Star not a Tree?
- C语言复习
- 写自己的ASP.NET MVC框架(上)
- 销售易
- SQLZOO(SELECT from WORLD Tutorial)Writeup
- 随机数的实现原理
- finally关键字的理解
- 系统重力感应开关和Activity的屏幕方向属性值之间的区别