浏览器抓取网页(用到一个神奇的库urllib.request.Request进行我们的模拟工作)
优采云 发布时间: 2022-03-18 22:29浏览器抓取网页(用到一个神奇的库urllib.request.Request进行我们的模拟工作)
urllib.error.HTTPError:HTTP 错误 403:禁止
从403 Forbidden我们可以发现,此时网站禁止程序的访问,这是因为csdn网站已经设置了反爬虫机制,当网站检测到一个爬虫,访问会被拒绝,所以我们会得到上面的结果。
这时候我们需要模拟浏览器访问,为了避开网站的反爬虫机制,然后顺利抓取我们想要的内容。
接下来,我们将使用一个神奇的库 urllib.request.Request 进行我们的模拟工作。这次我们先代码再讲解,不过这次要提醒一下,下面的代码不能直接使用。其中my_headers中的User-Agent替换为我自己的。因为我加了省略号保密,所以不能直接使用。替换方法如下图所示。这次为了使用方便,介绍一下功能:
#coding:utf - 8
from urllib.request import urlopen
from urllib.request import Request
import random
import re
def getContent(url,headers):
"""
此函数用于抓取返回403禁止访问的网页
"""
random_header = random.choice(headers)
"""
对于Request中的第二个参数headers,它是字典型参数,所以在传入时
也可以直接将个字典传入,字典中就是下面元组的键值对应
"""
req =Request(url)
req.add_header("User-Agent", random_header)
req.add_header("GET",url)
req.add_header("Host","blog.csdn.net")
req.add_header("Referer","http://www.csdn.net/")
content=urlopen(req).read().decode("utf-8")
return content
url="http://blog.csdn.net/beliefer/article/details/51251757"
#这里面的my_headers中的内容由于是个人主机的信息,所以我就用句号省略了一些,在使用时可以将自己主机的
my_headers = ["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/53 。。。Chrome/54.0.2840.99 Safari/537.36"]
print(getContent(url,my_headers))
使用上面的代码,我们就可以正常抓取到这个网页的信息了,接下来介绍如何获取我们的getContent函数中需要用到的headers中的参数。
既然我们要模拟一个浏览器访问网页,这些参数自然需要我们在浏览器中搜索。
首先我们点击进入要爬取的网页,然后右击页面,点击review元素,会出现如下框架,然后我们点击Network,然后我们会发现我们所在页面的信息确实不出现,没关系,这时候我们刷新页面,就会出现下图所示的信息。
这时候我们会在第一行看到51251757,这就是我们网页的URL后面的标签。这时候,当我们点击这个标签的时候,就会出现下图所示的内容:
这是我现在通过直接访问此 URL 获得的屏幕截图:
前两张图,我以前写的是版本2的访问,现在直接用了。当时我在csdn主页上点击了这个博客,所以我在代码中填写了referer,在我的header中填写了前两张图片。是的
,而这张图是我直接通过URL链接进入浏览器的,所以从图中可以看出,referer是,这是我们的网站,贴在这里是为了让大家更好的理解这个referer . 不一样的画面。在这张图中,我用红线标出了需要填写的四个内容。测试的时候千万不要使用我给的User-Agent,因为我用省略号替换了一些。每个人都应该用自己的方式来弥补。
这时候我们会找到Headers,有没有亮眼的感觉,是的,你的直觉是对的,我们需要的信息就在这个Headers里面。
然后,根据代码中需要的参数,把信息复制回来使用,因为这里显示的信息正好对应key值,所以我们复制使用非常方便。
现在介绍一下这个urllib.request.Request的用法(翻译自官方文档):
class urllib.request.Request ( url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None ) 参数: url:不用说,这是我们要访问的URL,它是一个字符串。数据:数据必须是一个字节对象,指定要发送到服务器的附加数据,如果不需要,则为无。目前,只有 HTTP 请求使用数据;当提供数据参数时,HTTP 请求应该是 POST 而不是 GET。数据应以标准 application/x-www-form-urlencoded 格式缓冲。urllib.parse.urlencode() 函数采用映射或二进制序列并返回该格式的 ASCII 字符串。当用作数据参数时,应将其编码为字节。(我们暂时不会使用这个,所以不管它) headers:headers 是一个字符的典型数据。当使用键和值参数调用 add_header() 时,标头将作为请求处理。该标头通常用于防止爬虫访问服务器。header 是浏览器用来标识自己的,因为有些 HTTP 服务器只允许来自普通浏览器的请求,而不是脚本(可以理解为爬虫)。
这必然增加了网站服务器的处理负担,即一个网站必须在爬虫检测和网站服务器的计算负担之间做一个权衡,所以不是爬虫检测机制越严格越好,还要考虑服务器的负担。origin_req_host:origin_req_host 应该是原创事务请求的主机,由 RFC 2965 定义。默认为 http.cookiejar.request_host(self)。
这是用户发起的原创请求的主机名或 IP 地址。例如,如果请求是针对 HTML 文档中的图像,则这应该是请求收录图像的页面的主机。(我们一般不用这个,这里就知道了) unverifiable:unverifiable 应该表示请求是否不可验证,由 RFC 2965 定义,默认值为 false。无法验证的请求是指无法提交用户的 URL。例如,当用户在网页的html文档中找到一张图片,但用户没有权限从服务器获取图片时,此时unverifiable应该为true。method:method 应该是一个字符串,指示要使用的 HTTP 请求方法(例如“header”)。如果提供,它的值存储在方法属性中,并通过方法 get_method() 调用。子类可以通过设置类的方法属性来指示默认方法。(这个基本不用)说了这么多无聊的定义,自己翻译都受不了了。让我们继续回到我们的程序:对于我们的程序,只需抓住几个要点。首先,我们需要构造一个请求:req =Request(url),这个时候请求是空的,我们需要在里面添加信息,给浏览器看。
req.add_header("User-Agent", random_header) 告诉网络服务器我正在通过浏览器访问,我不是爬虫。req.add_header("GET",url)是告诉浏览器我们访问的URL,req.add_header("Host","")是网站的信息,我们从网站开始随便填一下,req.add_header("Referer","")这句话很重要,它告诉网站服务器我们在哪里找到了我们要访问的网页,比如你点击了百度如果一个link跳转到当前访问的页面,referer是百度中的链接,是一种判断机制。对标头的构造函数也可以这样做:
#coding:utf - 8
from bs4 import BeautifulSoup
from urllib.request import urlopen
from urllib.request import Request
import random
import re
def getContent(url,headers):
"""
此函数用于抓取返回403禁止访问的网页
"""
random_header = random.choice(headers)
"""
对于Request中的第二个参数headers,它是字典型参数,所以在传入时
也可以直接将个字典传入,字典中就是下面元组的键值对应
"""
# req =Request(url)
# req.add_header("User-Agent", random_header)
# req.add_header("GET",url)
# req.add_header("Host","blog.csdn.net")
# req.add_header("Referer","http://www.csdn.net/")
header = {"User-Agent": random_header, "GET": url, "Host": "blog.csdn.net", "Referer": "http://www.csdn.net/"}
req=Request(url,None,header)
content=urlopen(req).read().decode("utf-8")
return content
url="http://blog.csdn.net/beliefer/article/details/51251757"
#这里面的my_headers中的内容由于是个人主机的信息,所以我就用句号省略了一些,在使用时可以将自己主机的User-Agent放进去
my_headers = ["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/53。。。(KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"]
print(getContent(url,my_headers))
上面程序中看到,我们也可以直接构造header,但是这样做有一个缺陷,就是header中的User-Agent被写死了。其实我们可以发现,对于不同的电脑主机访问同一个网页时,我们的其他三个信息:GET、Host、Referer可能都是一样的。这时候就只有User-Agent作为判断用户异同的标准,那么问题来了,如果我们“借”一些同学的User-Agent来使用,岂不是更好玩?模拟多用户访问?其实这也是我刚开始的代码里的原因,所以有一个my_headers的列表,里面其实可以放多个User-Agent,然后通过random函数随机选择一个进行组合,然后创建一个用户,实现多访问的假象其实很有用。要知道,对于一个网站,当访问次数过多时,用户的ip会被屏蔽。这一点都不好玩,所以如果你想永久访问一个网站而不被抓住,还是需要很多技巧的。
当我们要爬取一个网站的多个网页时,由于主机访问频繁,很容易被网站检测到,进而被屏蔽。而如果我们把更多不同的主机号放在列表中,随意使用,是不是很容易被发现呢?当然,当我们为了防止这种情况,更好的方法是使用IP代理,因为我们不容易被发现。可以获得很多主机信息,IP代理也很容易从网上搜索到。关于多次访问的问题我会在以后的博客中解释,这里就不多说了。