真·从零开始使用Requests制作简单爬虫

2019-12-20  本文已影响0人  TK丰

Requests是什么?

我个人理解的是,Requests是一个基于python的urllib模块的封装包。对比与python自带的urllib模块,requests的代码理解起来更人性化、更流畅(不过这个特点慢慢被urllib3赶上了)。

我可以用Requests做什么?

见名释义,Requests就是用来请求网络资源用的,例如访问网址,下载视频等。早期很多python爬虫都是基于Requests来实现的,我曾经用Requests做过多个爬虫以及刷投票的爬虫,本文就带大家做一个最简单的爬虫,从0学会使用Requests。这里着重强调一下:我反对大家用爬虫去做任何违法的事,爬虫写得爽,监狱进得早(严肃脸)。

为什么说是早期的爬虫呢?因为现在python的爬虫工具越来越多、越来越强大,Requests需要大量的代码才能实现的功能,对于scapy这种爬虫工具来说不过几行代码的事,所以大家慢慢也不再使用Requests做爬虫。但这个不碍于你学习Requests,并领略他的有趣之处。

安装Requests

pip install requests 

这里注意不要漏了Requests最后的s,因为Request是另外一个库

检验是否安装成功

打开CMD,输入python,随后输入

import requests

按回车,如果没有报错信息,则代表已经安装好了

一般报错信息为:ImportError: No module named requests


Requests的基础用法

访问一个网页

r = requests.get('https://www.jianshu.com/p/a82e76881121')
print (r.content) #打印获得网页内容。你也可以使用r.text的方式

一般我们使用Requests的时候,只会用到get或者post两种方式。
简单的科普一下:get的请求方式一般是不附带请求表单。如果进行请求直接附带在链接后类似http://x.x.com?id=1
post的方式一般是附带请求表单,不会展示在链接后
附带w3school-get以及post的比较

返回的信息

403代表的是禁止访问。我们可以通过r.status_code来看返回的访问码

这个码有什么用呢?以前我也觉得没啥用,当有次我在连续爬取某个网站的内容并保存在本地,执行完后,我随机抽看第1000+个的文件,发现后续保存的内容都有问题,因为中途爬虫被识别出来了,后续保存的信息都是错误的。之后我加入了code的判断->本次爬取获得的code跟上次获得的不一样并且不是200,代表爬虫不再适用,就会停止爬虫并报警。

print (r.status_code)
#附带基本的code含义
#200 正常响应
#302 重定向
#403 禁止访问
#404 没有这个网址
#500 服务器没有响应
#502 服务器没有响应

现在一般的网站都有防爬虫机制,所以我们需要给爬虫制作一个user agent,来伪装成一个浏览器

我有一个文件保存了1000+个不同浏览器的user agent,但简书不支持上传附件。为什么要这么多?以后你就知道了

userAgent =  "Mozilla/5.0 (Linux; Android 6.0; PLK-AL10 Build/HONORPLK-AL10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043305 Safari/537.36 MicroMessenger/6.5.6.1020 NetType/WIFI Language/zh_CN"
headers = {
  'User-Agent' : userAgent #伪装成火狐的手机浏览器
}
r = requests.get('https://www.jianshu.com/p/a82e76881121',headers = headers)
print (r.content)
正确获得了网页内容

headers是告诉服务器你是谁,你从什么网页过来,你做了什么等,headers的内容不单只有一个user-agent,可以通过在Chrome"右键"->“检查”->"网络",点击某一个请求来看当前的headers是什么,然后把程序请求的headers变成浏览器的即可


怎样把获得的内容提取出你需要的信息,一般get获得的方式都是以网页内容为主,你可以使用正则匹配,或者多次切割字符串的方式来获取。如果获取的是json数据,那就可以直接用访问json的方式获取。

一般值得爬的网站,类似小说网等,他们的链接都是有一定的规律的,于是我们通过自动化去生成链接的方式,让程序不断执行上面的get方式,然后保存获得的。

向网站提交表单(POST)

#并不能正确登录,只是做一个简单的例子
#data对象是你要提交的数据,key和value
r = requests.post('https://accounts.douban.com/login', data={'form_email': 'abc@example.com', 'form_password': '123456'}

其实post和get在Requests上区别并不大,所以并不细讲post。什么时候用get,什么时候用post不是取决于你,而是取决于服务器对当前链接是采用get还是post,利用查看header的方式可获得请求的方式。


接下来我爬取<诛仙>来展示一下整个流程,以及讲解一下里面的注意地方

打开网站
找到链接间的规律

发现下一章地址就是当前章的地址加1。另外,这个网站并没有做防爬限制,所以大家可以很开心的开始爬,是的就这么简单就做好一个爬虫了。

import requests, time
url = 'http://www.xbiquge.la/1/1693/%d.html' #把链接不变的地方构造出来,%d代表的是把这个位置预留出来,之后填补数字进去
thisHerf  = 1269354 #当前页面的地址
filename = '第%d章.txt' #保存成文本,也是预留一个位置之后填补数字
n = 1 #用来保存文章的时候填充章节数
while 1:
    r = request.get(url%thisHerf) #获取对应页面内容,%代表的是把数字放到%d这个位置。
    thisHerf += 1 #下一个页面的地址 # 当前地址加1,获取下一个地址
     with open (filename % n, 'w') as f: #把获取到的网页内容保存成txt文件。
         f.write(r.content) #这里有一个字符问题,大家可以自行研究一下
     n += 1 #下一个章节数
     time.sleep(3) #停止3秒再爬下一章,做个好人

除了%d之外,还有%s,%c等,具体大家可以百度一下占位符即可。
with open里的参数给大家一个参考:

r :只能读已存在的文件;
r+:可读可写,不会创建不存在的文件。从顶部开始写,会覆盖之前此位置的内容; 
w+ :可读可写,如果文件存在,则覆盖整个文件不存在则创建; 
w :只能写,覆盖整个文件,不存在则创建; 
a: 只能写,从文件底部添加内容,不存在则创建; 
a+: 可读可写,从文件顶部读取内容,从文件底部添加内容,不存在则创建

well,本来不想告诉大家的,其实这个脚本是有问题的XD。问题如下:

  1. 保存这种网页编码本来就是比较反人类,正常人类无法阅读,需要二次处理字符问题。
    2.到了一定的章节数,网址其实变化了,跳跃了一大段(小说网站通常都这样),不再连续了。也就是说,后续下载的都是错的文章
    那我们那要怎么解决这些问题呢?请保持好奇心,继续往下看

思路

一、文章转码,正确显示文章
二、找到“下一章”这3个字的标签,获取它对应的链接

不再是构建链接的方式,而是获取下一章链接的方式

一、文章转码,正确显示文章

s = str(r.content, 'utf8')
print (s)
加这句前:
服用前
加了之后:
服用后

二、找到“下一章”这3个字的标签,获取它对应的链接

1.找到“下一章”的一种方法

可以通过切割的方式。所谓切割方式,就是根据你想要的字符,在出现该字符的地方切一刀(切多少刀可以由你自己决定)。随后根据获得的内容继续切割,直到获得所需要的字符串。

keyWord = '下一章'
#方法一:切割大法,只保留把“下一章”前的内容
s1 = s.split('下一章')[0] #简单理解split的作用是将“下一章”作为切割点,把字符串切成两块,0代表只保留第一块,1代表第二块
print (s1)#看看有啥

展示末尾部分截图


切割后的结果末尾

很明显了,就是把“章节目录”后的那部分切割好就行了

#这里我一步到位切割了
nextHref = s1.split('章节目录')[1].split('href="')[1].split('">')[0]  
print (nextHref)
# 结果输出:/1/1693/1269356.html

split获得的是列表,所以采用[0]、[1]的方式提取。我们这里没有指定切割多少刀,默认只要出现"章节目录"的地方都切一刀,所以split有时候会获得多个元素。附上split的用法

2.找“下一章”的另一种方法

用正则匹配的方式定位并获取对应的内容

keyWord = '下一章'
#方法一:匹配大法,直接寻找“下一章”的标签
import re
#compile里的是正则表达式,具体语法百度就好.
nextHref = re.compile(r'(?<=章节目录</a> &rarr; <a href=").*?(?=">下一章)')
l = nextHref.findall(s) #用findall的方式,得到的是列表形式
print (nextHref) # ['/1/1693/1269356.html', '/1/1693/1269356.html']
#提取出来就可以用了

python的re模块不支持变长的后发断言,?<=以及?=后都需要有定长匹配,不然可以直接用(?<=章节目录.*? href=").*?(?=">下一章)效果更佳

整合代码

等等,为什么没有提取正文内容的讲解0.0?因为提取正文内容这部分是与提取“下一章”链接的用法是一致的。这里我建议大家直接使用split就可以了
其实不提取也不影响阅读,要更个性化的用户体验就需要自己动手了XD

没有提取正文,直接展示整个文章
import requests, time, re

# host用于跟我们获得下一章的链接组装成正确的下一章链接
host = 'http://www.xbiquge.la/' 
targetHref = 'http://www.xbiquge.la/1/1693/1269354.html'# 第一章的地址

# 保存成文本,也是预留一个位置之后填补数字
filename = '第%d章.txt' 
n = 1 # 用来保存文章的时候填充章节数

while 1:
    r = requests.get(targetHref) # 获取对应页面内容,%代表的是把数字放到%d这个位置。
    # 对文章内容进行转码
    s = str(r.content, 'utf8')

    # 找到下一章的方法一
    #s1 = s.split('下一章')[0]
    #nextHref = s1.split('章节目录')[1].split('href="')[1].split('">')[0]  
    
    # 找到下一章的方法二
    nextHrefRgx = re.compile(r'(?<=章节目录</a> &rarr; <a href=").*?(?=">下一章)')
    l = nextHrefRgx.findall(s) #用findall的方式,得到的是列表形式
    # 组装下一章的链接并赋值给targetHref
    targetHref = host + l[0]

    with open (filename % n, 'w') as f: #把获取到的网页内容保存成txt文件。
        f.write(s) # 把已转码的内容写入
    n += 1 #下一个章节数
    time.sleep(3) #停止3秒再爬下一章,做个好人
  
部分结果截图

代码还有个地方没有完善,没有跳出这个循环的break语句。大家可以自行思考一下如何控制跳出循环


That‘s all,Thank you for reading it

上一篇下一篇

猜你喜欢

热点阅读