第055讲: 论一只爬虫的自我修养3:隐藏 | 学习记录(小甲鱼零基础入门学习Python)

(标答出处: 鱼C论坛)
《零基础入门学习Python》


本节知识点:


上节课说过,有一些网站比较痛恨爬虫程序,它们不喜欢被程序所访问,所以它们会检查链接的来源,如果说来源不是正常的途径,那么它就会把你给屏蔽掉,所以呢,要让我们的程序可以持续的干活,要可以投入生产,我们就需要对代码进行隐藏,让它看起来更像是普通人浏览器的正常点击。

我们知道,服务器检查链接是通过检查 链接中的 Headers 中的 User Agent 来判断你是来自于代码还是来自于浏览器,像我们的Python,你用Python默认的Headers 中的 User Agent 是Python 加上版本号,服务器一检查是Python,就会把你屏蔽掉。我们可以修改 Headers 来模拟正常的浏览器访问。

先看一下文档:
在这里插入图片描述
urllib.request.Request 有一个 headers 的参数,通过修改 headers 参数,你可以设置自己的 headers,这个参数是一个字典,你可以通过两种途径来设置:

一:是你直接设置一个字典,然后作为参数传给 Request,
二:在Request 生成之后,调用 add_header() 将其加进去。

我们使用上节课的例子来尝试一下:

第一种 代码清单:

#translation.py
import urllib.request
import urllib.parse
import json
print("---------这是一个Python翻译器---------")
        
content = input('请输入需要翻译的内容:')
 
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
#直接从审查元素中copy过来的url会报错,必须把translate_o中的_o 删除才可以
#url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
 
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
 
data = {}            #这里就是把 Form Data 中的内容贴过来
data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '15445124815349'
data['sign'] = 'a824eba4c23c6f541ffadfee26b1e500'
data['ts'] = '1544512481534'
data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_REALTIME'
data['typoResult'] = 'false'
 
#需要使用urllib.parse.urlencode() 把data转换为需要的形式
data = urllib.parse.urlencode(data).encode('utf-8')
 
req = urllib.request.Request(url, data, head)  
response = urllib.request.urlopen(url, data)
html = response.read().decode('utf-8')
 
target = json.loads(html)
 
print('翻译结果:%s' %(target['translateResult'][0][0]['tgt']))

第二种 代码清单:

#translation.py
import urllib.request
import urllib.parse
import json
print("---------这是一个Python翻译器---------")
        
content = input('请输入需要翻译的内容:')
 
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
#直接从审查元素中copy过来的url会报错,必须把translate_o中的_o 删除才可以
#url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
 
#head = {}
#head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
 
data = {}            #这里就是把 Form Data 中的内容贴过来
data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '15445124815349'
data['sign'] = 'a824eba4c23c6f541ffadfee26b1e500'
data['ts'] = '1544512481534'
data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_REALTIME'
data['typoResult'] = 'false'
 
#需要使用urllib.parse.urlencode() 把data转换为需要的形式
data = urllib.parse.urlencode(data).encode('utf-8')
 
req = urllib.request.Request(url, data)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36')
response = urllib.request.urlopen(url, data)
html = response.read().decode('utf-8')
 
target = json.loads(html)
 
print('翻译结果:%s' %(target['translateResult'][0][0]['tgt']))

运行结果:

=========== RESTART: C:\Users\XiangyangDai\Desktop\translation.py ===========
---------这是一个Python翻译器---------
请输入需要翻译的内容:爱
翻译结果:love
>>> req.headers
{'User-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}

我们来回顾一下,修改 headers 有两个途径

  • 通过 Request 的 headers 参数修改
  • 通过 Request.add_header() 方法修改

修改 User-Agent 可以算是最简单的隐藏方法了,也是切实可行的,不过呢,如果是用 Python 抓取网页,例如批量下载 图片,你一个 IP 地址短时间内连续的进行访问,那么这是不符合正常人类的标准的,而且对服务器带来的压力不小,所以服务器合情合理会把你屏蔽掉,屏蔽的做法也很简单,只需要记录每个IP的访问频率,在单位时间内,如果访问频率超过一个阈值,那么服务器就会认为这个IP很有可能是一个爬虫,那么服务器就会把不管它的User-Agent是什么了,服务器就会返回一个验证码的界面,因为用户会填写验证码,但是爬虫不会,这就会合理的把你的访问给屏蔽掉。

就我们目前的学习水平来说,有两种做法可以解决这种问题,

一:延迟提交的时间,让我们的爬虫看起来更像是一个正常的人类在浏览(这是没有办法的办法);
二:使用代理。

第一种方法,我们可以使用 time 模块来完成延迟操作:(这种方法的工作效率太慢了)

#translation.py
import urllib.request
import urllib.parse
import json
import time
 
print("---------这是一个Python翻译器---------")
 
while True:       
        content = input("请输入需要翻译的内容(输入'Q'退出程序):")
        if content == 'Q':
                break
 
        url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
        #直接从审查元素中copy过来的url会报错,必须把translate_o中的_o 删除才可以
        #url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
 
        #head = {}
        #head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
 
        data = {}            #这里就是把 Form Data 中的内容贴过来
        data['i'] = content
        data['from'] = 'AUTO'
        data['to'] = 'AUTO'
        data['smartresult'] = 'dict'
        data['client'] = 'fanyideskweb'
        data['salt'] = '15445124815349'
        data['sign'] = 'a824eba4c23c6f541ffadfee26b1e500'
        data['ts'] = '1544512481534'
        data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
        data['doctype'] = 'json'
        data['version'] = '2.1'
        data['keyfrom'] = 'fanyi.web'
        data['action'] = 'FY_BY_REALTIME'
        data['typoResult'] = 'false'
 
        #需要使用urllib.parse.urlencode() 把data转换为需要的形式
        data = urllib.parse.urlencode(data).encode('utf-8')
 
        req = urllib.request.Request(url, data)
        req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36')
        response = urllib.request.urlopen(url, data)
        html = response.read().decode('utf-8')
 
        target = json.loads(html)
 
        print('翻译结果:%s' %(target['translateResult'][0][0]['tgt']))
        time.sleep(5)  #暂停5秒钟  

第二个方案:使用代理

首先需要知道,代理是什么?

嘿,兄弟,哥们儿访问这个网址有点困难,帮忙解决一下呗!
然后你就把需要访问的网址告诉代理先生,代理帮你访问,然后把他看到的所有内容原封不动的转发给你。
这就是代理。

代理的工作原理就是这么简单,因此呢,服务器看到的IP地址就是代理的IP地址,而不是你的IP地址,这样子你用很多个代理同时发起访问,服务器也没有办法。使用代理的步骤如下:

- 1.参数是一个字典 {‘类型’ :‘代理ip: 端口号’}

proxy_support = urllib.request.ProxyHandler({})

它这个参数就是一个字典,字典的键就是代理的类型(如:http,https…),值就是对应的 ip 或者 域名 +端口号

- 2.定制、创建 一个 opener

(opener可以看做是私人订制,当你使用 urlopen 打开一个普通网页的时候,你就是在使用默认的 opener 来工作,而这个opener 是可以有我们定制的,例如我们可以给它加上特殊的 headers,或者指定 代理,我们使用下面的语句定制、创建一个 opener)

opener = urllib.request.build_opener(proxy_support)

- 3a.安装opener

我们使用 urllib.request.install_opener(opener),把它安装到系统中,这是一劳永逸的做法,因为在此之后,你只要使用普通的 urlopen() 函数,就是使用定制好的 opener 进行工作,如果你不想替换掉默认的 opener,你可以使用下面的语句调用 opener。

- 3b.调用opener

  opener.open(url)

我们来举个例子:

我们需要代理ip,直接在网上搜索即可。

import urllib.request
 
url = 'http:/www.whatismyip.com.tw'  #查询ip的网站
 
proxy_support = urllib.request.ProxyHandler({'http': '123.206.56.247:80'})
 
opener = urllib.request.build_opener(proxy_support)
 
urllib.request.install_opener(opener)
 
response = urllib.request.urlopen(url)
html = response.read().decode('utf-8')
 
print(html)

测试题:


在这里插入图片描述
答:通过发送的 HTTP 头中的 User-Agent 来进行识别浏览器与非浏览器,服务器还以 User-Agent 来区分各个浏览器。
在这里插入图片描述
答: 在网络信息的传输中会出现偶然的“丢包”现象,有可能是你发送的请求服务器没收到,也有可能是服务器响应的信息不能完整送回来……尤其在网络阻塞的时候。所以,在设计一个“称职”的爬虫时,需要考虑到这偶尔的“丢包”现象。
在这里插入图片描述
答:我们之前说 HTTP 是基于“请求-响应”模式,Request 即请求的意思,而 Response 则是响应的意思。由客户端首先发出 Request,服务器收到后返回 Response。
在这里插入图片描述
答:add_header() 方法往 Request 对象添加 headers。
在这里插入图片描述
答: 将信息传给代理服务器,代理服务器替你向你要访问的服务器发送请求,然后在将服务器返回的内容返回给你。

因为有“丢包”现象发生,所以多了一个中间人就意味着会多一层发生“丢包”的几率,且大多数代理并不只为一个人服务,尤其是免费代理。

PS:大家想做“坏坏”的事情时可以考虑多几层代理,一般来说路由器日志并不会保存很长时间,几层代理后,基本很难查到是谁请求的。

在这里插入图片描述
答:使用 get_method() 方法获取 Request 对象具体使用哪种方法访问服务器。最常用的无非就是 GET 和 POST 了,当 Request 的 data 参数被赋值的时候,get_method() 返回 ‘POST’,否则一般情况下返回 ‘GET’。
在这里插入图片描述
答: 是 cookie,服务器通过判断你提交的 cookie 来确定访问是否来自”熟人“。

简单来说 cookie 可以分成两类:

  • 一类是即时过期的 cookies,称为“会话” cookies,当浏览器关闭时(这里是 python 的请求程序)自动清除
  • 另一类是有期限的 cookies,由浏览器进行存储,并在下一次请求该网站时自动附带(如果没过期或清理的话)

动动手:


小甲鱼打算在这里先给大家介绍一个压箱底的模块 —— Beautiful Soup 4

翻译过来名字有点诡异:漂亮的汤?美味的鸡汤?呃……

好吧,只要你写出一个普罗大众都喜欢的模块,你管它叫“Beautiful Shit”大家也是能接受的……

Beautiful Soup 是一个可以从 HTML 或 XML 文件中提取数据的 Python 库。它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。Beautiful Soup 会帮你节省数小时甚至数天的工作时间。

这玩意儿到底怎么用?

看这 -> 传送门

上边链接是官方的快速入门教程(不用惧怕,这次有中文版了),请大家花差不多半个小时的时间自学一下,然后完成下边题目。

噢,对了,大家可以使用 pip 安装(Python3.4 以上自带的神一般的软件包管理系统,有了它 Python 的模块安装、卸载全部一键搞定!)

打开命令行窗口(CMD) -> 输入 pip install BeautifulSoup4 命令) -> 搞定。

0. 编写一个爬虫,爬百度百科“网络爬虫”的词条
(链接 -> http://baike.baidu.com/view/284853.htm ),将所有包含“view”的链接按下边格式打印出来:
在这里插入图片描述
提示:题目中需要使用到简单的正则表达式(在官方的快速入门教程有演示)

import urllib.request
import re
from bs4 import BeautifulSoup
 
def main():
    url = "http://baike.baidu.com/view/284853.htm"
    response = urllib.request.urlopen(url)
    html = response.read()
    soup = BeautifulSoup(html, "html.parser") # 使用 Python 默认的解析器
    
    for each in soup.find_all(href=re.compile("item")):
        print(each.text, "->", ''.join(["http://baike.baidu.com", each["href"]]))
        # 上边用 join() 不用 + 直接拼接,是因为 join() 被证明执行效率要高很多
 
if __name__ == "__main__":
    main()

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

import urllib.request
import urllib.parse
import re 
from bs4 import BeautifulSoup
 
def main(): 
        #keyword = urllib.parse.quote(input ('请输入关键词:'))
        keyword = urllib.parse.quote('猪八戒')
        response = urllib.request.urlopen("http://baike.baidu.com/item/%s" % keyword)
        html = response.read().decode('utf-8')
        soup = BeautifulSoup(html, "html.parser")
        l = soup.find_all(href=re.compile("view"))
        for each in l :
                content = ''.join([each.text])
                url2 = ''.join(["http://baike.baidu.com", each["href"]])
                try:
                        response2 = urllib.request.urlopen(url2)
                        html2 = response2.read()
                        soup2 = BeautifulSoup(html2, "html.parser")
                        if soup2.h2:
                                content = ''.join([content, soup2.h2.text])
                                content = ''.join([content, " -> ", url2])
                                print(content)
                except UnicodeError as identifier:
                        pass
 
if __name__ == "__main__":
        main()

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

import urllib.request
import urllib.parse
import re 
from bs4 import BeautifulSoup
 
def test_url(soup):
    result = soup.find(text=re.compile("百度百科尚未收录词条"))
    if result:
        print(result[0:-1]) # 百度这个碧池在最后加了个“符号,给它去掉
        return False
    else:
        return True
 
def summary(soup):
    word = soup.h1.text
    # 如果存在副标题,一起打印
    if soup.h2:
        word += soup.h2.text
    # 打印标题
    print(word)
    # 打印简介
    if soup.find(class_="lemma-summary"):
        print(soup.find(class_="lemma-summary").text)   
 
def get_urls(soup):
    for each in soup.find_all(href=re.compile("view")):
        content = ''.join([each.text])
        url2 = ''.join(["http://baike.baidu.com", each["href"]])
        try:
            response2 = urllib.request.urlopen(url2)
            html2 = response2.read()
            soup2 = BeautifulSoup(html2, "html.parser")
            if soup2.h2:
                content = ''.join([content, soup2.h2.text])
            content = ''.join([content, " -> ", url2])
            yield content
        except UnicodeError as identifier:
                        pass
 
def main():
    word = input("请输入关键词:")
    keyword = urllib.parse.urlencode({"word":word})
    response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
    html = response.read()
    soup = BeautifulSoup(html, "html.parser")
 
    if test_url(soup):
        summary(soup)
        
        print("下边打印相关链接:")
        each = get_urls(soup)
        while True:
            try:
                for i in range(3):
                    print(next(each))
            except StopIteration:
                break
            
            command = input("输入任意字符将继续打印,q退出程序:")
            if command == 'q':
                break
            else:
                continue
    
if __name__ == "__main__":
    main()

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38970783/article/details/88562748
今日推荐