爬虫取中间文本_【实战No.2】1小时打造你自己的网络爬虫

    

新年好

爬虫离我们生活很近,诺基亚时代正是爬虫的帮助才成就了那么多网络小说。同时,它也是一门很简单的技术,但可能涉及到编程,很多朋友就认为学之不易。

前天David来南京“跨年”,我就找到以前看网课的笔记,1小时帮助他完成了自己爬虫的编写,借着机会我也就将其整理成文,各位集中精力,抽两局吃鸡的时间不仅可以掌握一个有用的小技术,还可以熟悉python语言特性,产生对编程的兴趣。

爬虫原理

那我们现在开始计时。

首先,大家先打开链接

www.anaconda.com/download

下载对应系统的python3.7安装包,我们边下载边介绍。

这个版本的python拥有丰富的类库,让我们的使用更加便捷。

37c6b60acd9a11dea3bde28699bba032.png

接着,我们谈谈为什么我们能爬取网页的信息。

大家打开自己的浏览器,随便搜索一个网址,比如news.baidu.com进入百度新闻的界面。

大家按F12打开浏览器的开发者工具,点击Sources可以看到左侧有个(index)的文件,里面写满了密集的代码。

对,这就是这个网页的源码,也即不考虑交互的话,如果你把这些代码复制粘贴了,就可以创建一个一模一样的网页界面。

8440be6c1f327362f40de8eab6320890.png

恩,还看到了百度的直男彩蛋。

bdad321253167a6bce087d2f693efad0.png

我们将网页下拉一点,用鼠标点击开发者工具左上角的那个方块+鼠标的图标,然后将鼠标挪到网页上的一条新闻上,发现开发者工具的代码也在对应变化,这里对应到一个:

人民日报评论员:...

我们爬虫爬的就是两个<>里面的文字内容,而这些<>就是网页的元素。

c47c0e40ee42893b224774ac9a4bd222.png

爬虫的原理就是通过网页源码的元素定位,然后来获取里面相应的文字内容,就是这么简单。

说到这里大家应该下载好了,点击安装包按默认设置安装。

等待安装的时间让我们来介绍网页的基本框架。

html

大家都听过html,这是一种网页框架协议,全称为Hyper Text Mark-up Language,也即超文本标记语言,不只是文本。

html的基本框架如下,大家可以在桌面新建一个txt文件,把这些代码复制并保存文件关闭后,将文件重命名为demo.html,打开就是一个网页的界面,可以看到,这里网页的元素是由这样前后闭合的标签组成的。

指定网页的标题

但这样的网页是没有灵魂的。

网页需要有整齐的排版,大部分网页都是利用DIV(division)将自己分成很多块;

fbec9258e7d6ed2cf2735186a88a6bad.png

网页也需要有人机交互,所以网页加入了很多表单元素;

交互和排版的需要,使得网页源码里充满了有结构性的元素,这种结构组成了网页的DOM结构树。

9320152f0ac536a82a8a8741d35640da.png

并且这些元素还有着对应的标签名、id、类名和属性。如下图,p和div都是标签名,class是类名,id是id值(网页id值写的话就唯一)。

p标签

   

新年好

最后,也是给我们启发的一点,网页排版后要利用CSS对元素进行配色等前端设计,需要我们先对元素先进行定位。

CSS里面有着非常便捷的选择器,主要包括三大基本选择器和基于结构的扩展选择器。以上图为例,想要将"新年好"的颜色设置成红色,我们可以这么写(更多扩展选择器在PPT有介绍):

5b6732f4a99778317fc20a8529a3b4f7.png

之后我们的爬虫就是依据这样的原理快速定位并提取文本信息。

jupyter notebook

现在大家应该安装完毕,准备开始编写爬虫了。

本文展示的案例是Frank之前写文献综述时总需要去知网查询实在嫌麻烦,就爬取了《保险研究》期刊历史的论文标题、作者、摘要、期次,并将其导出到excel表,只要在excel搜索即可定位。最后效果如下:

aabdb308a0262940c7d934ccbcdd3873.png

我们先打开anaconda界面里的jupyter notebook,这是一种便捷的笔记本,可以进行代码的运行。

825cd80010016a26354b6b3e27629c9e.png

点击右上角的new--> Python3新建我们第一个python文件

jupyter notebook代码是填写在一个个cell里面,代码写完后按Ctrl+Enter即可执行,点击图标"+"可以往下添加新的cell,便于给代码按功能分块

938e76bb6a29c6edd6a7cc422589c4ab.png

爬取逻辑

接着,我们打开网址,搜索保险研究并进入官网。

爬取数据要先观察一条数据和整个数据的逻辑关系,再开始着手进行代码书写。千里之行,就始于这第一条我们需要的数据。

这里大家直接点击官网左侧的过刊浏览,在新页面点击上方数字的01期。

a32464a3bfdc455d9c633e870938b8a8.png

在新界面按下F12打开上面提到的开发者工具,点击左侧上方的鼠标选择器,并移到第一篇文章的标题上,下方自然出现了对应的网页源码。

e11d2694e232949293e997dc17afd94f.png

我们注意到这个标题是在标签下的

标签的标签里,借助上面我们提到的CSS选择器工具,可以很快定位获取指定数据。

再想办法将这个提取方法运用到这一期的所有文章,并最后将所有期次汇总即可。

e74563db83f3bfcf612b1afc54f4afc5.png

之后编写代码过程我们仍要不断在这个网页观察如何准确定位元素。

书写代码

下面我们就来代码实现这个方法,大家按照图中代码书写即可,强调一下所有的符号都是英文标点:

0b44f015c9a171c0a55631a60fd1ef17.png

我们先导入requests的包,利用requests.get方法可以获取网页地址,复制粘贴刚才2018年01期的网址:

http://bxyj.cbpt.cnki.net/WKE/WebPublication/wkTextContent.aspx?colType=4&yt=2018&st=01

在python语言中加上双引号/单引号是用来告诉函数这是一个字符串而不是变量。

接着自定义res变量来接收传入的网页,用print函数来进行输出(代码运行是按Ctrl+Enter),发现无内容,因为这里返回的是网页对象,print一下res.text发现输出了网页的源码。python是用#来添加注释的。

885f868e39e58dbc9a7373f9de3e932a.png

这里提一下为什么是request.get(url)。

浏览器向服务器传输数据的方式分为POST和GET两种,POST传输依靠表单项,比如传输账号密码;GET传输则是将参数直接附在地址栏后传输,适用于安全性要求低的传输,百度搜索就是用这个原理。

885f868e39e58dbc9a7373f9de3e932a.png

刚才print有大段输出,我们在代码前加#注释再运行隐去输出结果。

4254ea7cbdc53fa6a40ccadd898a903b.png

获取了网页的源码,接下来我们导入BeautifulSoup包(据说源自爱丽丝漫游奇境记的甲鱼汤),它能帮我们便捷定位和获取文本内容。自定义变量soup来接上网页的源码,用"html.parser"告诉这是一个html框架的源码。

我们用soup.select可以利用前面介绍的CSS选择器方法快速定位到目标元素。

我们先来获取文章标题,在这个过程中需要我们不断观察刚才的页面源码,利用F12+选择器分析如何准确定位到指定元素。

e11d2694e232949293e997dc17afd94f.png

Frank发现第一个文章的标题在

标签的

标签下,所以用soup.select方法,里面填写上面提到CSS扩展选择器中的后代选择器,这里填写"li h3"。

4254ea7cbdc53fa6a40ccadd898a903b.png

print一下发现输出的是图中标记3的部分,是一个包含该页所有文章标题的列表。

list(列表)是python的一种容器,用"[ ]"从0开始依次存储各种元素,元素之间用","隔开。

用list[数字]可以得到指定位置上的元素。这里title[0]即获取列表第一个元素,也就是我们要的第一个标题。

我们print一下title[0],这次的输出结果在标记2的位置,发现得到了第一个标题,可是仍然包含了html的元素格式。

BeautifulSoup提供了text方法,我们只要在title[0]后加.text,print一下,标记1处终于输出了想要的文本。

83d56dd44918b5b24e4a169ac4bc9129.png

我们按照这种方法,能够依次获取标题、作者、摘要。

需要注意的是这三个都是列表,并且三个列表长度相同、列表同一位置的元素同属于一篇文章,这样在后面不会产生错位。

18d4008b0e444e207d177d8b6feb97c1.png

其中日期是Frank观察到先通过CSS选择器的类名选择器来指定到,再用a[href="#"]指定到它的特殊属性这样更精确,也能帮助大家熟悉各种选择器。

需要注意的是因为外面用双引号表示文本,所以里面的#需要用单引号指定文本,不然双引号会提前闭合。

接下来,我们需要有一个字典接入对应我们获取的内容。

d80edbcaf5087b6658e75a5875ee370b.png

字典也是python的一种容器,和list容器的有序排列不同,字典通过{"键":"值"}的方式来存储数据。类比到excel上,第一行就是我们的键,下面就是我们要存储的值。

a20d162288607d76cf915157016ae7f2.png

python可以通过 字典["键名"]来设置和获取对应的值,和excel中一行数据有多个属性一样,一个字典也可以存入多个键值对。

上面提到过标题、作者、摘要是三个长度相同、位置对应的列表。

那么只要我们指定一个循环,依次从三个列表提取元素放到字典,再将这些字典一起放到列表,就可以将01期所有文章信息都提取出来。这里用到了for循环。

9366bf16156e875d81131e7f183a8981.png

for循环是python的基本循环之一,可以通过指定变量和范围来设置循环次数,每次循环结束变量+1,直到变量超过这个范围停止循环。

这里设置了变量i,让其在0到len(title)的范围循环,len(title)输出的是列表的长度,也即元素个数,range(0,len(title))指定了一个大于等于0但小于(不等于)len(title)的范围,当i超过这个范围循环停止。

每次循环我们都将paper设置为空字典,往里面加上列表i位置的文本,由于都是一期的文章所以date和i无关。

因为paper每次循环都被清空,所以在循环外我们新建了一个空列表paper_list,用列表的append方法存取字典。

写完输出,发现01期的文章收录成功。

成功在望

离目标只差一步。

我们只要将01期的这个方法(也可以叫函数)运用到所有期次就能大功告成。

这就牵涉到两个问题:

Q1.如何将代码封装成一个方法?

Q2.如何获取所有期次?

先解决Q1:

在python里通过如下格式即可开始定义我们的方法。

def 方法名(传入参数):

...

return 传出参数

python严格而优美的排版要求方法和循环下的每行都要空一个tab的空位,即四个空格。

在这个例子里面,方法需要传入的参数是网页地址url,传出参数是paper_list

但是在定义方法的时候,我们并没有传入一个真正的网址,而是自定义一个变量名来代替要传入的网址形式,我们将其称之为形式参数。后续传入真正的网址叫做实际参数。

fa5d2f32dead5bb95d3e08d226e7e7e9.png

我们这里的形参叫做content,这个content代替之前操作的01期《保险研究》网址,按照之前的流程,只要在requests.get获取,之后写上之前的代码,最后return出paper_list。

方法定义好之后我们测试一下,将01期的网址赋值给url,用getcontents(url)将实参传入,发现代码输出了和之前一样的结果。

现在来看Q2:如何获取所有期次?

每个网站的设计都有自己的逻辑,我们这个网站的逻辑是什么呢?

url = "http://bxyj.cbpt.cnki.net/WKE/WebPublication/wkTextContent.aspx?colType=4&yt=2018&st=01"

大家仔细看看我们这个地址,发现结尾处有yt=2018,st=01。

这难道就是用GET方法向网站发送了一个2018年01期的请求吗?

Frank测试了一下发现果然如此,那么接下来就好办多了

只要每次向上面的getcontents方法传入带有这种规律的url作为实际参数,再将每期的输出用一个新的列表存储就可以了。

上面提到url是作为一个字符串传入,python的format可以很方便的对字符串的指定内容进行替换。我们先将url的yt=和st=的部分挖去并填入{},{}在这里起到一个标记的作用。

eab368fc03a33ccaadb335fa7a9250db.png

再利用url.format()的方法传入指定参数到标记好的{}位置,有几个{}传入几个参数,这里需要两个。

最后我们分别对年份和月份循环,每次循环都生成一个新的url,并调用getcontents(url)来获取这个url也即《保险研究》某期的文章内容。   

这里 s = "%02d"%j是因为在月份为个位数的时候传入参数是1而不是01,所以用"%02d"%j将j变成两位的十进制数,不够两位在左边补0。

写完运行,因为网页比较多,所以需要代码先飞一会。左边显示In[*]表示程序正在运行,运行完毕后*会变成数字。数据太多就不print了,用len看看是否生成了那么多期。

输出数据

数据都获取了,可以着手输出了。

不过这里大家要注意一个以后会经常用到的trick。

最早我们用字典存入了一个文章的元素

paper=

{“期次”:“201801期”, “标题”:  ”XXXXXX”,  “作者”: “David Zhao”,   “摘要”: ”大猪蹄子”}

后来我们用getcontens()返回的列表接存储了很多字典

paper_list = [paper1,paper2,paper3…]

之后我们又写了一个对url的循环,并且用列表存储,生成了:

total = [paper_list1, paper_list2…]

这就意味着total是两层列表:

total = [

[paper11,paper12,paper13…], [paper21,paper22,paper23…]...

]

而python输出到excel二维表的数据的格式应该是

[paper11,paper12,paper13...paper21,paper22,paper23...]

这样excel才能将每个字典同名的键提取出来作为表头,将具有相同键的值依次放入行中。

所以我们要去掉一层列表,不卖关子,直接用两层for循环:

792d9f9181a305656d1d0409f3106444.png

第一次for循环读取total的paper_list,第二次循环读取paper_list的paper,最后将其放入total列表当中,这样就形成了可以导出到excel的数据。

这里for循环没有用in range(),而是直接用 in 列表,这是一种python的语法结构,因为列表是有序的,所以用in 列表的形式可以依次获取列表元素,而不需要再用list[i]的方法。前面没有直接这样写是帮助大家熟悉python的语法规则。

4d1d5c959a8cdbb0b15be1e085719490.png

最后利用pandas包导出。pandas的名称来自于面板数据(panel data)和python数据分析(data analysis),主要用于经济金融的分析。

3b6f70707135c7e765c4943466167d0b.png

回到jupyter notebook的网页主界面,按时间排序一下发现生成了我们命名的文件,双击下载并打开,调整一下格式即可。

aabdb308a0262940c7d934ccbcdd3873.png

至此,案例结束,David理解并打完,掐表一共花了1个小时多一丢丢。

一个图梳理一下我们的操作:

a04cc9d527e6174cb005c3b177d3e610.png

结语

以上就是一个网络爬虫的小案例,我们发现爬虫其实很简单,只要梳理好网页逻辑,能够利用CSS选择器快速定位到元素,以及熟悉字符串的操作就能快速爬取数据。

难点是在后面对数据的清洗分析,现在Frank暂时放下金融去达内学Java大数据了,后续有空再给大家继续做教程。

以一个爬虫为线索,Frank在这里给大家介绍了html原理、CSS语法规则、python的列表、字典、循环、方法定义,以及很多小trick。

希望能帮助大家了解爬虫,喜欢上编程这个工具。本文一带而过的一些术语,比如DOM、扩展选择器等我都整理了一个PPT,大家在公众号后台回复“爬虫”即可获取网盘链接。

最后欢迎大家转发讨论~~

附上所用的代码截图:

166513528f8ddd6a89032db4797cba9a.png

5857399b939f9a69a535ac5a31458269.png

fc1d81b07686ec3dc0fc00852eb91137.png 76dc6af458c4f566b7ac97223a503625.png 6db07b66451d21159b45ccb86818652e.gif

找对象扫码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值