用python抓取网页数据
金蝶云社区-墨家总院
墨家总院
3人赞赏了该文章 926次浏览 未经作者许可,禁止转载编辑于2018年11月21日 19:59:10

(本文独家发布在金蝶云社区上)

楔子

今年北京实施了积分落户政策,作为北漂老鸟当然要尝试一下,虽然自知积分达标的概率不高,但是万一还有奇迹出现呢。等了大半年,终于在十月份的时候,北京市第一次积分落户尘埃落定。结果早已预料到,鄙人没有达标。但是看到了名单公示网站,我突发奇想,用python把这些公示名单抓下来,正好练练python手艺。


进行了些调查后发现,积分落户的公示页面的网页找不到一点数据,才发现有些网页数据是动态获取,即网页上的资源数据都是现成从另外的地方获取到的,比如积分落户这种(现在已经十一月份,发现链接已经失效了,就不在这列出来了)。还有一种是静态网页,资源都是硬写在代码里的。我们先从静态数据抓取开始。


静态网页数据抓取


有些网站的数据是直接嵌在网页里的,用python的网页解析函数BeautifulSoup就可以一点一点得分析网页结构,最后将需要的信息抓取下来。比如前几个月,我想从CMU机器学习系的网站上下载他们历年的研究生写得数据分析工(DAP)的报告论文,但是pdf文件特别多,一个一个下载太累,就萌生了写python抓取的想法。

Screen Shot 2018-11-11 at 17.46.24.png


首先先把需要的函数包import进来,大约需要解析url的urllib和上面提到BeautifulSoup。


import urllib.request

from urllib.parse import urljoin

from urllib.parse import urlparse

from bs4 import BeautifulSoup


这里主要说一下代码编写步骤。一般情况下,不同的网页有不同的网页结构,所以代码不可能写成通用的,只能根据不同的需求改代码。一般第一步都是打开页面,加载到一个BeautifulSoup对象中:


base = "https://www.ml.cmu.edu/research/data-analysis-projects.html";

response = urllib.request.urlopen(base)

html = response.read()

soup = BeautifulSoup(html, "lxml")

response.close()


然后用循环语句枚举soup对象里的所有html元素,我这个例子里需要寻找的目标是超链接指向pdf,所以就要找html里的href标签:



for i, link in enumerate(soup.findAll('a')):

       _FULLURL = urljoin(base, link.get('href')

       #Process your pdf links


具体细节就不说了,这个小工具代码在这里:fetch_pdf.py


动态网页抓取


动态网页抓取就不太需要BeautifulSoup来枚举网页结构了,因为静态页面上是没有任何数据的。每次刷新网页,网页内嵌的脚本都会从另外一个地方获取数据。动态网页数据最关键的就是通过一些工具窥探到网页是如何获取数据的,然后将相关信息获取到,写到代码里。

动态网页数据的抓取虽然神秘,其实用到的库比较简单,首先是urllib里的request,主要用来实现http协议的请求服务。

所以我们只需要三个库就能完成任务,首先是上面提到的reguest,然后是csv,数据抓下来,先简单地存为csv格式。最后是json库,目前网上查询到的结果都是以json数据格式提供的,所以需要json库来解析查询结果。

import urllib.requestimport csvimport json

接下来,我们就从网页中要获取三个最重要的东西,即获取数据的url,cookie和header。获取的步骤是,首先用chrome浏览器打开我们需要获取数据的网站,因为目前积分落户的公示页面已经下架,所以不能用来测试了,所以我用了https://dujia.qunar.com这个网站,然后打开Chrome自带的 Developer Tools:


Screen Shot 2018-11-11 at 16.56.52.png




然后点击去哪儿网上的一个分类,就能看到Developer Tools下的Network选项里会出现很多内容,然后再点击XHR tab,就会看到我们希望看到的fetch类型的请求信息:


Screen Shot 2018-11-11 at 16.53.44.png


可以往上拉一点看到URL信息:


Screen Shot 2018-11-11 at 17.11.10.png



通过以上的信息,我们可以获得request的URL字符串,以及cookie,以及其他header信息。


import urllib.request

import json


url = 'https://dujia.qunar.com/golfz/searchbox/recommend?callback=jQuery17208743913254480413_1541927412748&dep=%E6%96%B0%E5%8A%A0%E5%9D%A1&modules=aDomestic%2CaAbroad%2CaAroundQuery%2CaSubject%2CallDefaultQuery%2CaOutRecom&_=1541927412955'


cookie = 'QN1=ezu0oVvn6hG6mJ2E2RKtAg==; QN205=organic; QN277=organic; csrfToken=4y3VWxHcCyaIeWjfstESabE7XVUKqz9b; activityClose=1; _i=VInJOf4bvQwVSAZ3Z2CHuBPXos1q; QN269=B62536D1E51B11E895ADFA163E1159C7; QunarGlobal=10.86.213.150_-388f59bf_16701dd313c_4d7f|1541925401124; QN99=109; QN300=organic; fid=5d1d57d1-cafa-4ddb-9847-9f4b080ba90f; _pk_id.1.678d=dfe81b24b16a3d43.1541925402.1.1541925508.1541925402.; JSESSIONID=61F7FB4A7EC66BE7C1FC7BEA5732CBC8; _vi=t-GjNHCd4dmH7rbWjQLO2MIatlwZ-_ZMz29wcuyr9GRHyZBvTOzONvKfu5epCOb8OXz4O5JVaJwQcR5Pj4aa190-PqfdVaWwLMWcg15X5ozb9UScqIiq1oRvEQZ0N6jI3kYTIbXA6zAYnpVKSTbRyedS9Ilk2zKuJSTLSBAPNANI; QN120=5YyX5Lqs%7C%7C%7C; PHPSESSID=jkoc9494gsgjtk0bak1fa3l173; QN48=tc_7f29b70ddce01a34_1670208f7e8_14ce; QN243=12; QN271=10e0c87e-4031-4ff1-ad38-fe7d11f57900; QN267=880766639cca0d984'


headers = { 'User-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',

          'Cookie': cookie,

          'Connection': 'keep-alive',

               'Accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01',

                   'Accept-Encoding': 'gzip, deflate, br',

                   'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6,de;q=0.5',

          'Host': 'dujia.qunar.com',

               'Referer': 'https://dujia.qunar.com/p/abroad?tf=dj_abroadnav' }


req = urllib.request.Request(url, None, headers)

response = urllib.request.urlopen(req)

content = response.read()


print(content.decode('utf8', 'ignore'))


得到的打印结果为:


jQuery17208743913254480413_1541927412748({"ret":true,"data":{"aAbroad":["马来西亚","新加坡","普吉岛","泰国","巴厘岛","曼谷","日本","印度尼西亚","首尔","槟城","吉隆坡","苏梅岛","菲律宾","芽庄","挪威","越南","兰卡威","马六甲","印度","迪拜","柬埔寨","热浪岛","柔佛","韩国"],"aAroundQuery":[],"allDefaultQuery":{"all":[],"ticket":[],"visa":[],"travel":[],"liner":[],"around":[]},"aDomestic":["周边","丽江"],"aOutRecom":["三亚","泰国","丽江","香港","厦门","日本","普吉岛","马尔代夫","上海迪士尼","长隆","九寨沟","巴厘岛","云南","台湾","桂林","北京","成都","杭州","青岛","塞班岛","张家界","上海","新加坡","大理","哈尔滨","西双版纳","乌镇","西安","昆明","邮轮"],"aSubject":["主题乐园"]},"status":0});


这个数据看起来没啥用,但是上面已经把动态抓取数据的关键几步说出来了。虽然目前北京市积分落户的公示页面下架了,我的代码不能运行了,我还是将代码show一下:FetchSettlePeopleBeijing.ipynb

参考

python抓取网页中的动态数据

赞 3