美文网首页
真·从零开始使用Requests制作简单爬虫

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

作者: TK丰 | 来源:发表于2019-12-20 13:12 被阅读0次

    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

    相关文章

      网友评论

          本文标题:真·从零开始使用Requests制作简单爬虫

          本文链接:https://www.haomeiwen.com/subject/zsvmnctx.html