2

数据通灵术之爬虫技巧

 2 years ago
source link: https://cosx.org/2017/08/web-scrap-tools/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

数据通灵术之爬虫技巧

关键词:爬虫; python; R 语言

审稿:郎大为、何通;编辑:雷博文

俗话说” 巧妇难为无米之炊”。如果你是一个数据忍者,却因为没有数据而烦恼,这卷” 数据通灵术” 或许是你需要的。首先你要看透术名那华丽的外衣,它的真面目是:爬虫技巧。

此卷通灵术包含了爬虫的基础入门术,动态加载破解术,登陆破解术,以及额外赠送的手机 APP 爬取篇。

简单来说,爬虫就是从网上自动下载网页,经过解析处理得到你想到要的数据。 这里的步骤和关键词有两个: 下载, 解析。本文的所有技巧也都是围绕这个两个关键词。

大多数的爬虫都是爬取网页上的数据。新手入门,就先搞清楚网页到底是什么。网页的源代码其实就是纯文本,包含了 HTML, CSS 和 JavaScript 。

  • HTML: 标记语言,只有语法,没有变量和逻辑,不能称之为编程语言。

  • CSS: 层叠样式表,控制元素的展现形式

  • JavaScript: 脚本语言,可以动态操作 HTML 中元素的增删改

一般来说,数据是在 HTML 元素中 (否则你看不见它)。详细的 HTML 介绍可以参考 W3School 的 HTML 教程

在 R 语言或 Python 中下载网页是很简单的。以下的两行代码,使用 R 的readLines函数读取了豆瓣电影 Top 250 的网页源码:

html_lines = readLines('https://movie.douban.com/top250')
doc = paste0(html_lines, collapse = '')

其他的 R 包也有类似的函数,如RCurl::getURLhttr::GET。Python 中的标配是 requests 模块。读文档,不细讲。

下载后的纯文本是类似下边的 HTML 标签,然而你需要的只是电影名称。

<a href="https://movie.douban.com/subject/1292052/" class="">
  <span class="title">肖申克的救赎</span>
  <span class="title"> / The Shawshank Redemption</span>
  <span class="other"> / 月黑高飞(港)  /  刺激1995(台)</span>
</a>

解析术就是将所需数据抽取出来的技巧。接下来介绍三种方法: 正则表达式,Xpath 和 CSS 选择器。这些技巧都是通用的,基本不需要考虑编程语言的选择,都会支持的。

正则表达式

正则表达式是通过描述文本规则来达到抽取目的。承接上文的豆瓣电影,使用正则来抽取电影名字:

# 匹配包含class="title"字符的行 无关正则
title_lines = grep('class="title"', html_lines, value = T)
# 用正则抽取>字符和<字符中间的字符
titles = gsub('.*>(.*?)<.*', '\\1', title_lines, perl = T)

如果想看看第二行的效果,可以试试下边的代码:

gsub('.*>(.*?)<.*', '\\1', 
     '<span class="title">肖生克的救赎</span>', perl = T)

其中.*>(.*?)<.*是一个正则表达式,其匹配规则如下图所示。 regularexp

这个例子包含了正则中的通配符,贪婪匹配和懒惰匹配,以及分组的概念。看你骨骼惊奇,送你这本正则表达式 30 分钟入门教程秘籍。学成归来之后,再来读读 R 语言中的正则表达式吧。

XPath

XPath 是 XML 路径语言,适用于 HTML 和 XML 这两种标记语言。了解 HTML 的树状结构之后,便自可得其精髓。

下边的代码使用xml2::read_html函数解析下载的网页源代码,接下来使用 XPath 语言寻找所有包含 class="title" 属性的 span 标签。

library(xml2)
dom = read_html(doc)
title_nodes = xml_find_all(dom, './/span[@class="title"]')
xml_text(title_nodes)
 [1] "肖申克的救赎"
 [2] "The Shawshank Redemption"
 [3] "这个杀手不太冷"
 [4] "Léon"
 [5] "霸王别姬"
 [6] "阿甘正传"
 ......

深入学习 XPath 可参考 HTML DOM 教程XPath 教程

CSS 选择器

CSS 选择器是通过标签的 CSS 属性达到筛选的目的。类似于 XPath,它同样需要先将纯文本解析成 DOM 文档,再进行选择操作。

这里借用rvest包来实现,筛选出class="title"的标签并拿到标签内的文本。

library(rvest)
read_html(doc) %>% 
  html_nodes('.title') %>% # class="title"的标签
  html_text()

读读 CSS 元素选择器教程,可以学到更多用法。另外,这个一边学一边练习的小网站也很带感呢。

三种技巧相比之下,XPath 和 CSS 选择器明显简单易用,但它们只适用于 HTML 和 XML 文档。正则表达式虽然规则复杂,但及其强大。利剑在手,任君选择。

工具破解篇

掌握前文提到的下载函数和解析术,就足以抓取大部分的网页了。但江湖险恶,网页叵测,你很可能会遇到以下常见的问题:

  • 在网页中看到的数据,下载后看不到了?(动态加载数据)

  • 数据分散在多个网页,但没有列表页也不知道网页链接的生成规则,难以遍历抓取

  • 需要登录才有查看权限,没办法直接下载 (登录验证)

接下来就介绍一些工具和技巧,一一破解上述问题。

破解动态加载数据

当你下载网页得到正常的结果 (status code 等于 200),却看不到想要的数据时,那么它通常都不在原始的 HTML 网页中,是通过二次请求得到。

判断数据是否包含在 HTML 网页中的方法很简单:在 Chrome 浏览器下打开网页后,右键,点击” 查看源代码”(View Page Source),搜索是否有你想到的数据。

如果没有,调出 Chrome 的开发者模式 (结印Ctrl + Shift + I,或者 Mac 上的Cmd + Opt + I),并切换到Network选项卡。再次访问网页,就可以看到网页加载过程的所有请求。其中包含了 HTML 文件,js 文件和 cc 文件,以及可能的图片和音视频文件等。接下来就是一个个查看这些请求的 Response,寻找数据是从哪个请求中返回的。

比如这篇 COS 访谈第 31 期: Charles Stein 博文的评论,是通过 iframe 加载得到的:

此处应有Chrome开发者模式的图

因此直接访问 https://d.cosx.org/embed/419227-cos-31-charles-stein 就可以拿到该博客的评论了。

如果你想亲自练练手,这个莆田医院分布图展示了莆田医院在中国各省市的分布,其中医院的列表是通过 Ajax 加载得到的。试试看能否找到那个链接?

通常来说,需要爬取的信息都分散在不同的页面,可以通过列表页的链接到达。比如新闻和简历的抓取。

然而有时候并没有一个明显的列表页面,只能通过点击得到动态的列表。这种情况下,列表的数据一般也是通过 Ajax 加载得到的。比如这个东风标志的经销商页面,目标是搜集所有经销商的地址,经纬度和电话等信息。而每个经销商的信息都散落在各自的详情页,比如这个页面。有兴趣的读者可以自行钻研,再查看接下来的答案。

使用 Chrome 的开发者模式探索网站后,可以找出以下的步骤和接口:

  1. 访问首页,抓取省市和响应的数字编码,比如北京市对应 3361,河北省对应 3363。

  2. 根据省的数字编码,获取市区的数据编码。比如河北省各市的数字编码:http://dealer.peugeot.com.cn/ajax.php?pid=3363&action=city。查看源码可以看到石家庄市的数字编码是 3394。

  3. 根据市区的数字编码,抓该市的经销商列表。比如石家庄市的两家经销商:http://dealer.peugeot.com.cn/ajax.php?cid=3394&action=dealer。从中可以拿到两个对应的字符编码,河北盛威汽车贸易有限公司的编码是 HBSWQCMYYXGS。

  4. 由经销商的编码,进入到该经销商的详情页: http://dealer.peugeot.com.cn/dealer/HBSWQCMYYXGS。接下来就使用解析术抽取响应的地址和电话等信息即可。提示,地图中的经纬度数据,可以搜索在源码中搜索 BMap.Point 看到。

# 部分网页源代码: 城市和相应的编码
<option value="3361">北京市</option>
<option value="3362">天津市</option>
<option value="3363">河北省</option>

最后,将 4 步串接起来,前三步可以拿到所有经销商列表和相应的编码,最后遍历所有的详情页抓取详细信息即可。

破解登陆状态

某些网站的信息会要求登录后才有权限查看。当你填完表单,点击登录时,浏览器的背后会发送一个 POST 请求,将你的用户名和密码发送到服务器进行验证。通过验证后,会在你的浏览器中存下 Cookie 来记录登录状态,后续的请求中只要带上这个 Cookie 就不需要再次登录。

因为要破解登录状态验证,最直接的方法就是模拟发送 POST 请求,拿到权限。然而模拟登陆是一个极有对抗性的话题,简单的网站或许就是一个 POST 请求。而复杂的网站如新浪微博,登陆过程可能经过层层加密,多个请求前后依赖,少一步都不可。因此更简单的方法是,在浏览器中登录,再拿相应的 Cookie 到脚本里直接用。

说来复杂,但操作简单。你需要的,只是这套提取 Cookie 和模拟网页请求的利器组合: Chrome + CurlWget + uncurl/ curl2r

CurlWget

CurlWget 是一个将浏览器请求转换为 curl 或者 wget 命令的工具 (curl 和 wget 是两个终端下常用的浏览器)。通过它你可以拨开浏览器的外衣,看到一个网络请求的真面目: 访问的 url 地址,携带的 Cookie 和 Header 的配置。

具体操作是,在 Network 选项卡里选中某个请求,右键,单击Copy->Copy as cURL。如图,单击后,该请求的 curl 命令便存贮在粘贴板里了。

此处应有图

接下来在命令行中,直接粘贴命令并运行即可 (据说看到数据在滚动时很有黑客的感觉,但我们是忍者,怎么能因为这个就沾沾自喜?)。

uncurl & curl2r

uncurlcurl2r 分别是将刚刚的 curl 命令转化为 Python 命令和 R 命令的工具。因为在抓取后我们通常需要做进一步的解析,这时候使用 Python 或 R 会方便做进一步的处理。

其用法很简单,在浏览器中点击”Copy as cURLs” 之后,在终端运行下面的命令即可。

$ uncurl "拷贝进来curl的命令"
requests.get("http://weibo.com/u/xxxxxxxxx/home?topnav=1&wvr=5",
    headers={
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, sdch",
        "Accept-Language": "zh-CN,zh;q=0.8,en;q=0.6,de;q=0.4",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "DNT": "1",
        "Pragma": "no-cache",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36"
    },
   cookies={
        "xxxxxx": "xxxxx",  # 我是不会暴露自己的cookie的...
        "_s_tentry": "login.sina.com.cn",
        "wvr": "6"
    },
)

其中第一个参数是微博主页的链接 (涉及到的微博 ID 已打码);接下来的 headers 中包含了一些请求参数,如”Accept” 代表接受返回的数据类型 (包括 HTML 和 image 等),”User-Agent” 是使用的设备和浏览器版本等;cookies 里则是当前网站在浏览器中存下的数据,可能记录了你的登录状态和浏览轨迹等,通常是经过加密的,属于隐私数据。

curl2r 的示例不再展示,可到 Github 的项目上查看。此术虽然方便,但需牢记,不可泄漏你的 Cookie 信息。

手机 APP 篇

移动互联网的兴起,导致一些企业着重 APP 应用,却不提供 PC 端的网站服务。对于手机 APP 而言,比较难查看流量请求。虽然也有对应的软件,但据我所知好用的都是收费的。而且难以通用在安卓和 IOS 两大平台。

这里介绍另一个工具: Charles。通过它,可以在 PC 端开启一个代理,把手机设备设置通过代理上网,所有 HTTP(S) 流量都会在该软件中一览无余。Charles 是一款免费,且在 Windows,Mac 和 Linux 上通用的跨平台软件。不论任何手机型号都可以连接 Charles 代理,可以说是很完美的解决方案了。

安装并开启 Charles 后,可以在Help -> Local IP Address里查看本级 IP 地址。接下来用手机接入同一个 WIFI,填入 Charles 的 IP 地址和 8888 端口,配置 HTTP 代理。

proxy

配置成功后,手机访问的 HTTP(S) 流量便会在 Charles 中展示出来。如需查看 HTTPS 的流量,需要安装 SSL 证书,并在Proxy -> SSL Proxying Settings...中设置添加信任的网站列表。

如下图,将 appi.51talk.com 添加到信任列表后,打开 51talk 的 APP 时可以看到收藏的英语外教列表。右侧的 Response 中展示了老师的姓名,头像链接,口音链接和得分等。切换到 Overview 标签栏,就可以看到相应的接口地址和其他请求参数。

image

如果想练练手的话,可以试试查看国际版微博。最后你会发现接口极其简单,只要拿到一个密匙,就可以直接在电脑端直接访问接口。没有任何 User Agent 或者 Cookie 的验证。

最后,此术只能查看 HTTP(S) 的流量。不要试图用它偷窥微信和支付宝等涉及支付的机密 APP。

掌握以上术式后,你的数据召唤术已达到中忍级别。且容在下多嘴一句,不要短时间内高频率的爬。如果给网站服务器造成压力,你离被封杀也就不远了。友好的爬虫是KPI提升的重要组成部分,你好我好,大家好。

掉入R坑有多年,如今回首已惘然;也曾痴心于Python,偶尔兼职写前端。现任51talk算法工程师,探索教育行业里的数据价值。杜亚磊

敬告各位友媒,如需转载,请与统计之都小编联系(直接留言或发至邮箱:[email protected]),获准转载的请在显著位置注明作者和出处(转载自:统计之都),并在文章结尾处附上统计之都微信二维码。

统计之都微信二维码

← 为什么统计学家也应该学学 TensorFlow Julia 中的分布式计算 →

发表/查看评论


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK