如何用Python爬数据?(一)网页抓取

你期待已久的Python网络数据爬虫教程来了。本文为你演示如何从网页里找到感兴趣的连接和说明文字,抓取并存储到Excel。php

需求

我在公众号后台,常常能够收到读者的留言。css

不少留言,是读者的疑问。只要有时间,我都会抽空尝试解答。html

可是有的留言,乍看起来就不明因此了。python

例以下面这个:git

一分钟后,他可能以为不妥(大概由于想起来,我用简体字写文章),因而又用简体发了一遍。github

我恍然大悟。web

这位读者觉得个人公众号设置了关键词推送对应文章功能。因此看了个人其余数据科学教程后,想看“爬虫”专题。shell

很差意思,当时我尚未写爬虫文章。数据库

并且,个人公众号暂时也没有设置这种关键词推送。编程

主要是由于我懒。

这样的消息接收得多了,我也能体察到读者的需求。不止一个读者表达出对爬虫教程的兴趣。

以前提过,目前主流而合法的网络数据收集方法,主要分为3类:

  • 开放数据集下载;
  • API读取;
  • 爬虫。

前两种方法,我都已经作过一些介绍,此次说说爬虫。

概念

许多读者对爬虫的定义,有些混淆。我们有必要辨析一下。

维基百科是这么说的:

网络爬虫(英语:web crawler),也叫网络蜘蛛(spider),是一种用来自动浏览万维网网络机器人。其目的通常为编纂网络索引

这问题就来了,你又不打算作搜索引擎,为何对网络爬虫那么热心呢?

其实,许多人口中所说的爬虫(web crawler),跟另一种功能“网页抓取”(web scraping)搞混了。

维基百科上,对于后者这样解释:

Web scraping, web harvesting, or web data extraction is data scraping used for extracting data from websites. Web scraping software may access the World Wide Web directly using the Hypertext Transfer Protocol, or through a web browser.

看到没有,即使你用浏览器手动拷贝数据下来,也叫作网页抓取(web scraping)。是否是马上以为本身强大了不少?

可是,这定义还没完:

While web scraping can be done manually by a software user, the term typically refers to automate processes implemented using a bot or web crawler.

也就是说,用爬虫(或者机器人)自动替你完成网页抓取工做,才是你真正想要的。

数据抓下来干什么呢?

通常是先存储起来,放到数据库或者电子表格中,以备检索或者进一步分析使用。

因此,你真正想要的功能是这样的:

找到连接,得到Web页面,抓取指定信息,存储。

这个过程有可能会往复循环,甚至是滚雪球。

你但愿用自动化的方式来完成它。

了解了这一点,你就不要老盯着爬虫不放了。爬虫研制出来,实际上是为了给搜索引擎编制索引数据库使用的。你为了抓取点儿数据拿来使用,已是大炮轰蚊子了。

要真正掌握爬虫,你须要具有很多基础知识。例如HTML, CSS, Javascript, 数据结构……

这也是为何我一直犹豫着没有写爬虫教程的缘由。

不过这两天,看到王烁主编的一段话,颇有启发:

我喜欢讲一个另类二八定律,就是付出两成努力,了解一件事的八成。

既然咱们的目标很明确,就是要从网页抓取数据。那么你须要掌握的最重要能力,是拿到一个网页连接后,如何从中快捷有效地抓取本身想要的信息。

掌握了它,你还不能说本身已经学会了爬虫。

但有了这个基础,你就能比以前更轻松获取数据了。特别是对“文科生”的不少应用场景来讲,很是有用。这就是赋能

并且,再进一步深刻理解爬虫的工做原理,也变得轻松许多。

这也算“另类二八定律”的一个应用吧。

Python语言的重要特点之一,就是能够利用强大的软件工具包(许多都是第三方提供)。你只须要编写简单的程序,就能自动解析网页,抓取数据。

本文给你演示这一过程。

目标

要抓取网页数据,咱们先制订一个小目标。

目标不能太复杂。可是完成它,应该对你理解抓取(Web Scraping)有帮助。

就选择我最近发布的一篇简书文章做为抓取对象好了。题目叫作《如何用《玉树芝兰》入门数据科学?》。

这篇文章里,我把以前的发布的数据科学系列文章作了从新组织和串讲。

文中包含不少以前教程的标题和对应连接。例以下图红色边框圈起来的部分。

假设你对文中提到教程都很感兴趣,但愿得到这些文章的连接,而且存储到Excel里,就像下面这个样子:

你须要把非结构化的分散信息(天然语言文本中的连接),专门提取整理,而且存储下来。

该怎么办呢?

即使不会编程,你也能够全文通读,逐个去找这些文章连接,手动把文章标题、连接都分别拷贝下来,存到Excel表里面。

可是,这种手工采集方法没有效率

咱们用Python。

环境

要装Python,比较省事的办法是安装Anaconda套装。

请到这个网址下载Anaconda的最新版本。

请选择左侧的 Python 3.6 版本下载安装。

若是你须要具体的步骤指导,或者想知道Windows平台如何安装并运行Anaconda命令,请参考我为你准备的视频教程

安装好Anaconda以后,请到这个网址下载本教程配套的压缩包。

下载后解压,你会在生成的目录(下称“演示目录”)里面看到如下三个文件。

打开终端,用cd命令进入该演示目录。若是你不了解具体使用方法,也能够参考视频教程

咱们须要安装一些环境依赖包。

首先执行:

pip install pipenv
复制代码

这里安装的,是一个优秀的 Python 软件包管理工具 pipenv 。

安装后,请执行:

pipenv install
复制代码

看到演示目录下两个Pipfile开头的文件了吗?它们就是 pipenv 的设置文档。

pipenv 工具会依照它们,自动为咱们安装所须要的所有依赖软件包。

上图里面有个绿色的进度条,提示所需安装软件数量和实际进度。

装好后,根据提示咱们执行:

pipenv shell
复制代码

此处请确认你的电脑上已经安装了 Google Chrome 浏览器。

咱们执行:

jupyter notebook
复制代码

默认浏览器(Google Chrome)会开启,并启动 Jupyter 笔记本界面:

你能够直接点击文件列表中的第一项ipynb文件,能够看到本教程的所有示例代码。

你能够一边看教程的讲解,一边依次执行这些代码。

可是,我建议的方法,是回到主界面下,新建一个新的空白 Python 3 笔记本。

请跟着教程,一个个字符输入相应的内容。这能够帮助你更为深入地理解代码的含义,更高效地把技能内化。

准备工做结束,下面咱们开始正式输入代码。

代码

读入网页加以解析抓取,须要用到的软件包是 requests_html 。咱们此处并不须要这个软件包的所有功能,只读入其中的 HTMLSession 就能够。

from requests_html import HTMLSession
复制代码

而后,咱们创建一个会话(session),即让Python做为一个客户端,和远端服务器交谈。

session = HTMLSession()
复制代码

前面说了,咱们打算采集信息的网页,是《如何用《玉树芝兰》入门数据科学?》一文。

咱们找到它的网址,存储到url变量名中。

url = 'https://juejin.im/post/5b339e99f265da597d0aac5f'
复制代码

下面的语句,利用 session 的 get 功能,把这个连接对应的网页整个儿取回来。

r = session.get(url)
复制代码

网页里面都有什么内容呢?

咱们告诉Python,请把服务器传回来的内容看成HTML文件类型处理。我不想要看HTML里面那些乱七八糟的格式描述符,只看文字部分。

因而咱们执行:

print(r.html.text)
复制代码

这就是得到的结果了:

咱们内心有数了。取回来的网页信息是正确的,内容是完整的。

好了,咱们来看看怎么趋近本身的目标吧。

咱们先用简单粗暴的方法,尝试得到网页中包含的所有连接。

把返回的内容做为HTML文件类型,咱们查看 links 属性:

r.html.links
复制代码

这是返回的结果:

这么多连接啊!

很兴奋吧?

不过,你发现没有?这里许多连接,看似都不彻底。例如第一条结果,只有:

'/'
复制代码

这是什么东西?是否是连接抓取错误啊?

不是,这种看着不像连接的东西,叫作相对连接。它是某个连接,相对于咱们采集的网页所在域名(https://www.jianshu.com)的路径。

这就好像咱们在国内邮寄快递包裹,填单子的时候通常会写“XX省XX市……”,前面不须要加上国家名称。只有国际快递,才须要写上国名。

可是若是咱们但愿得到所有能够直接访问的连接,怎么办呢?

很容易,也只须要一条 Python 语句。

r.html.absolute_links
复制代码

这里,咱们要的是“绝对”连接,因而咱们就会得到下面的结果:

这回看着是否是就舒服多了?

咱们的任务已经完成了吧?连接不是都在这里吗?

连接确实都在这里了,但是跟咱们的目标是否是有区别呢?

检查一下,确实有。

咱们不光要找到连接,还得找到连接对应的描述文字呢,结果里包含吗?

没有。

结果列表中的连接,都是咱们须要的吗?

不是。看长度,咱们就能感受出许多连接并非文中描述其余数据科学文章的网址。

这种简单粗暴直接罗列HTML文件中全部连接的方法,对本任务行不通。

那么咱们该怎么办?

咱们得学会跟 Python 说清楚咱们要找的东西。这是网页抓取的关键

想一想看,若是你想让助手(人类)帮你作这事儿,怎么办?

你会告诉他:

“寻找正文中所有能够点击的蓝色文字连接,拷贝文字到Excel表格,而后右键复制对应的连接,也拷贝到Excel表格。每一个连接在Excel占一行,文字和连接各占一个单元格。”

虽然这个操做执行起来麻烦,可是助手听懂后,就能帮你执行。

一样的描述,你试试说给电脑听……很差意思,它不理解。

由于你和助手看到的网页,是这个样子的。

电脑看到的网页,是这个样子的。

为了让你看得清楚源代码,浏览器还特地对不一样类型的数据用了颜色区分,对行作了编号。

数据显示给电脑时,上述辅助可视功能是没有的。它只能看见一串串字符。

那可怎么办?

仔细观察,你会发现这些HTML源代码里面,文字、图片连接内容先后,都会有一些被尖括号括起来的部分,这就叫作“标记”。

所谓HTML,就是一种标记语言(超文本标记语言,HyperText Markup Language)。

标记的做用是什么?它能够把整个的文件分解出层次来。

(图片来源:https://goo.gl/kWCqS6)

如同你要发送包裹给某我的,能够按照“省-市-区-街道-小区-门牌”这样的结构来写地址,快递员也能够根据这个地址找到收件人。

一样,咱们对网页中某些特定内容感兴趣,能够依据这些标记的结构,顺藤摸瓜找出来。

这是否是意味着,你必须先学会HTML和CSS,才能进行网页内容抓取呢?

不是的,咱们能够借助工具,帮你显著简化任务复杂度。

这个工具,Google Chrome浏览器自带。

咱们在样例文章页面上,点击鼠标右键,在出现的菜单里面选择“检查”。

这时,屏幕下方就会出现一个分栏。

咱们点击这个分栏左上角(上图红色标出)的按钮。而后把鼠标悬停在第一个文内连接(《玉树芝兰》)上面,点击一下。

此时,你会发现下方分栏里面,内容也发生了变化。这个连接对应的源代码被放在分栏区域正中,高亮显示。

确认该区域就是咱们要找的连接和文字描述后,咱们鼠标右键选择高亮区域,而且在弹出的菜单中,选择 Copy -> Copy selector。

找一个文本编辑器,执行粘贴,就能够看见咱们究竟复制下来了什么内容。

body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a
复制代码

这一长串的标记,为电脑指出了:请你先找到 body 标记,进入它管辖的这个区域后去找 div.note 标记,而后找……最后找到 a 标记,这里就是要找的内容了。

回到我们的 Jupyter Notebook 中,用刚才得到的标记路径,定义变量sel。

sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a'
复制代码

咱们让 Python 从返回内容中,查找 sel 对应的位置,把结果存到 results 变量中。

results = r.html.find(sel)
复制代码

咱们看看 results 里面都有什么。

results
复制代码

这是结果:

[<Element 'a' href='https://www.jianshu.com/nb/130182' target='_blank'>]
复制代码

results 是个列表,只包含一项。这一项包含一个网址,就是咱们要找的第一个连接(《玉树芝兰》)对应的网址。

但是文字描述“《玉树芝兰》”哪里去了?

别着急,咱们让 Python 显示 results 结果数据对应的文本。

results[0].text
复制代码

这是输出结果:

'玉树芝兰'
复制代码

咱们把连接也提取出来:

results[0].absolute_links
复制代码

显示的结果倒是一个集合。

{'https://www.jianshu.com/nb/130182'}
复制代码

咱们不想要集合,只想要其中的连接字符串。因此咱们先把它转换成列表,而后从中提取第一项,即网址连接。

list(results[0].absolute_links)[0]
复制代码

此次,终于得到咱们想要的结果了:

'https://www.jianshu.com/nb/130182'
复制代码

有了处理这第一个连接的经验,你信心大增,是吧?

其余连接,也无非是找到标记路径,而后照猫画虎嘛。

但是,若是每找一个连接,都须要手动输入上面这若干条语句,那也太麻烦了。

这里就是编程的技巧了。重复逐条运行的语句,若是工做顺利,咱们就要尝试把它们归并起来,作个简单的函数。

对这个函数,只需给定一个选择路径(sel),它就把找到的全部描述文本和连接路径都返回给咱们。

def get_text_link_from_sel(sel):
    mylist = []
    try:
        results = r.html.find(sel)
        for result in results:
            mytext = result.text
            mylink = list(result.absolute_links)[0]
            mylist.append((mytext, mylink))
        return mylist
    except:
        return None
复制代码

咱们测试一下这个函数。

仍是用刚才的标记路径(sel)不变,试试看。

print(get_text_link_from_sel(sel))
复制代码

输出结果以下:

[('玉树芝兰', 'https://www.jianshu.com/nb/130182')]
复制代码

没问题,对吧?

好,咱们试试看第二个连接。

咱们仍是用刚才的方法,使用下面分栏左上角的按钮点击第二个连接。

下方出现的高亮内容就发生了变化:

咱们仍是用鼠标右键点击高亮部分,拷贝出 selector。

而后咱们直接把得到的标记路径写到 Jupyter Notebook 里面。

sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a'
复制代码

用咱们刚才编制的函数,看看输出结果是什么?

print(get_text_link_from_sel(sel))
复制代码

输出以下:

[('如何用Python作词云?', 'https://juejin.im/post/5b34a409f265da599f68dc3b')]
复制代码

检验完毕,函数没有问题。

下一步作什么?

你还打算去找第三个连接,仿照刚才的方法作?

那你还不如全文手动摘取信息算了,更省事儿一些。

咱们要想办法把这个过程自动化

对比一下刚刚两次咱们找到的标记路径:

body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a
复制代码

以及:

body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a
复制代码

发现什么规律没有?

对,路径上其余的标记全都是同样的,惟独倒数第二个标记("p")后冒号后内容有区别。

这就是咱们自动化的关键了。

上述两个标记路径里面,由于指定了在第几个“子”(nth-child)文本段(paragraph,也就是"p"表明的含义)去找"a"这个标记,所以只返回来单一结果。

若是咱们不限定"p"的具体位置信息呢?

咱们试试看,此次保留标记路径里面其余所有信息,只修改"p"这一点。

sel = 'body > div.note > div.post > div.article > div.show-content > div > p > a'
复制代码

再次运行咱们的函数:

print(get_text_link_from_sel(sel))
复制代码

这是输出结果:

好了,咱们要找的内容,全都在这儿了。

可是,咱们的工做还没完。

咱们还得把采集到的信息输出到Excel中保存起来。

还记得咱们经常使用的数据框工具 Pandas 吗?又该让它大显神通了。

import pandas as pd
复制代码

只须要这一行命令,咱们就能把刚才的列表变成数据框:

df = pd.DataFrame(get_text_link_from_sel(sel))
复制代码

让咱们看看数据框内容:

df
复制代码

内容没问题,不过咱们对表头不大满意,得更换为更有意义的列名称:

df.columns = ['text', 'link']
复制代码

再看看数据框内容:

df
复制代码

好了,下面就能够把抓取的内容输出到Excel中了。

Pandas内置的命令,就能够把数据框变成csv格式,这种格式能够用Excel直接打开查看。

df.to_csv('output.csv', encoding='gbk', index=False)
复制代码

注意这里须要指定encoding(编码)为gbk,不然默认的utf-8编码在Excel中查看的时候,有多是乱码。

咱们看看最终生成的csv文件吧。

颇有成就感,是否是?

小结

本文为你展现了用Python自动网页抓取的基础技能。但愿阅读并动手实践后,你能掌握如下知识点:

  • 网页抓取与网络爬虫之间的联系与区别;
  • 如何用 pipenv 快速构建指定的 Python 开发环境,自动安装好依赖软件包;
  • 如何用 Google Chrome 的内置检查功能,快速定位感兴趣内容的标记路径;
  • 如何用 requests-html 包来解析网页,查询得到须要的内容元素;
  • 如何用 Pandas 数据框工具整理数据,而且输出到 Excel。

或许,你以为这篇文章过于浅白,不能知足你的要求。

文中只展现了如何从一个网页抓取信息,可你要处理的网页成千上万啊。

别着急。

本质上说,抓取一个网页,和抓取10000个网页,在流程上是同样的。

并且,从我们的例子里,你是否是已经尝试了抓取连接?

有了连接做为基础,你就能够滚雪球,让Python爬虫“爬”到解析出来的连接上,作进一步的处理。

未来,你可能还要应对实践场景中的一些棘手问题:

  • 如何把抓取的功能扩展到某一范内内的全部网页?
  • 如何爬取Javascript动态网页?
  • 假设你爬取的网站对每一个IP的访问频率作出限定,怎么办?
  • ……

这些问题的解决办法,我但愿在从此的教程里面,一一和你分享。

须要注意的是,网络爬虫抓取数据,虽然功能强大,但学习与实践起来有必定门槛。

当你面临数据获取任务时,应该先检查一下这个清单:

  • 有没有别人已经整理好的数据集合能够直接下载?
  • 网站有没有对你须要的数据提供API访问与获取方式?
  • 有没有人针对你的需求,编好了定制爬虫,供你直接调用?

若是答案是都没有,才须要你本身编写脚本,调动爬虫来抓取。

为了巩固学习的知识,请你换一个其余网页,以我们的代码做为基础修改后,抓取其中你感兴趣的内容。

若是能把你抓取的过程记录下来,在评论区将记录连接分享给你们,就更好了。

由于刻意练习是掌握实践技能的最好方式,而教是最好的学

祝顺利!

思考

本文主要内容讲解完毕。

这里给你提一个疑问,供你思考:

咱们解析而且存储的连接,实际上是有重复的:

这并非咱们的代码有误,而是在《如何用《玉树芝兰》入门数据科学?》一文里,原本就屡次引用过一些文章,因此重复的连接就都被抓取出来了。

可是你存储的时候,也许不但愿保留重复连接。

这种状况下,你该如何修改代码,才能保证抓取和保存的连接没有重复呢?

讨论

你对Python爬虫感兴趣吗?在哪些数据采集任务上使用过它?有没有其余更高效的方式,来达成数据采集目的?欢迎留言,把你的经验和思考分享给你们,咱们一块儿交流讨论。

喜欢请点赞。还能够微信关注和置顶个人公众号“玉树芝兰”(nkwangshuyi)

若是你对数据科学感兴趣,不妨阅读个人系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

相关文章
相关标签/搜索