您的位置:首页 > 理论基础 > 计算机网络

python3实现网络爬虫(5)--模拟浏览器抓取网页

2016-11-29 23:33 901 查看
      本来准备继续分析BeautifulSoup的,但是好多网页都是反爬虫的,想分析没法分析了 ,那么就跳一节吧,我们先看看如何模拟浏览器进行访问网页,然后再折回去继续说BeautifulSoup。

      由于前面我已经用python2写过这方面的内容了,那么这次偷个懒,我就在以前的博客上进行简单的移植了,这个博客的网址为:点击打开链接

下面是一个简单的访问:

#coding:utf-8
from urllib.request import urlopen

url="http://www.csdn.net/"
html=urlopen(url)
print(html.read().decode("utf-8"))


      使用上面的程序,将会访问到csdn网站的首页并将显示此网页的源码。
     我们再来看下面的例子:

#coding:utf - 8
from urllib.request import urlopen

url="http://blog.csdn.net/beliefer/article/details/51251757"
html=urlopen(url)
print(html.read().decode("utf-8"))
在此例子中,我将网址改变了,改成访问csdn中的博客,此时便出现了下面的结果:

Traceback (most recent call last):

  File "D:/python/python3.5/first.py", line 7, in <module>

    html=urlopen(url)

  File "D:\newinstall\python3.5\lib\urllib\request.py", line 163, in urlopen

    return opener.open(url, data, timeout)

  File "D:\newinstall\python3.5\lib\urllib\request.py", line 472, in open

    response = meth(req, response)

  File "D:\newinstall\python3.5\lib\urllib\request.py", line 582, in http_response

    'http', request, response, code, msg, hdrs)

  File "D:\newinstall\python3.5\lib\urllib\request.py", line 510, in error

    return self._call_chain(*args)

  File "D:\newinstall\python3.5\lib\urllib\request.py", line 444, in _call_chain

    result = func(*args)

  File "D:\newinstall\python3.5\lib\urllib\request.py", line 590, in http_error_default

    raise HTTPError(req.full_url, code, msg, hdrs, fp)

urllib.error.HTTPError: HTTP Error 403: Forbidden

     从其中的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里的参数。

       既然我们是要模拟浏览器进行网页访问,那么这些参数自然需要我们去浏览器中寻找了。

       首先我们点击进入将要爬取的那个网页,然后鼠标右击页面,点击审查元素,将会出现下面的的框架,然后我们点击Network,这时候会发现并没有出现我们所在的页面的信息,没关系,这时候我们刷新一下页面,便会出现如下图所示的信息了。



       这时候我们会看见第一行的51251757,而这正是我们网页的网址的后面的标号,这时候我们点击这个标号,便会出现下图所示的内容:



下面是我现在直接用这个网址访问得到的截图:



对于前两张图,我是以前写版本2的访问时的,现在直接拿过来用的,那个时候是在csdn首页点到这个博客的,所以在代码和前两张图中我header中的referer填的是

blog.csdn.net,而这张图是我直接用网址链接输入到浏览器访问的,所以从图片中可以看出,referer是http://blog.csdn.net/beliefer/article/details/51251757,这正是我们的网址,这里也是为了让大家更好地理解这个referer,才贴了不一样的图。在这张图片中,我已经将需要填写的四个内容用红色的线标记出来了,大家在测试的时候,一定不要用我给的那个User-Agent,因为我中间用省略号替换掉了一些,大家要用自己的补上去。

     这时候我们会发现Headers,是不是有种眼前一亮的感觉,没错,你的直觉是对的,我们所需要的信息正在这个Headers里面。

     然后对照着代码中的需要的参数,将这些信息拷贝回去便可以使用了,因为这里面显示的信息刚好是键值对应的,所以我们拷贝使用也就很方便了。

     现在来介绍下这个urllib.request.Request的用法(从官方文档中翻译过来 的):

class
urllib.request.
Request
(url,data=None,headers={},origin_req_host=None,unverifiable=False,method=None)参数:      url:这个就不必多说了,是我们即将访问的网址,它是一个字符串。      data:数据必须是一个字节的对象,它指定额外的数据发送到服务器,如果没有必要的话则为None。目前,只有HTTP请求使用data;当data参数被提供的时候,http请求应该是post而不是get方式。数据应该是标准application/x-www-form-urlencoded格式缓冲。
urllib.parse.urlencode()
函数的参数为映射或二元序列并返回该格式的ASCII字符串。它在被用作数据参数应该被编码到字节。(这个我们暂时用不到,就先别管它了)      headers:headers是一个字典型数据,当add_header()被调用且有key和value参数的时候,这个headers会被 当做请求进行处理。这个header通常用来防止爬虫的访问服务器的,header是浏览器用于识别本身的,因为一些HTTP服务器只允许来自常见的浏览器而不是脚本的请求(可以理解为爬虫)。例如,Mozilla Firefox浏览器可以识别自己为“Mozilla / 5(X11;U;Linux i686)Gecko/2008070208火狐20071127
/ 2.0.0.11”,而模块的默认用户代理字符串为“python urllib / 2.6“(Python 2.6),也就是在访问网页的时候,对于有反爬虫机制的网站是可以通过这个户代理字符串“python urllib / 2.6“(Python 2.6)检查出我们是爬虫的,然后对我们的访问进行拒绝,这也正是我们需要User-Agent进行改正的原因。在这里面,一个网站的反爬虫机制到底有多严格是要综合考虑的,因为你对爬虫的监控越严格,那么你需要检查的东西就越多,这就不可避免的增加了网站服务器的处理负担,也就是一个网站必须在爬虫检测和网站服务器的运算负担之间做一个权衡,所以并不是爬虫检测机制越严格越好的,还要考虑到服务器的负担。      origin_req_host:origin_req_host应该是原本交易的发出请求的主机,它是由RFC 2965定义的。它默认为
http.cookiejar.request_host(self)
。这是由用户发起的原始请求的主机名称或IP地址。例如,如果请求的是HTML文档中的图像,这应该是包含图像的页面请求的主机。(这个我们一般也不会用到,这里知道就好了)      unverifiable:unverifiable应该表明请求是否是无法验证,它是由RFC 2965定义的,默认值是false。一个无法验证的请求是指用户的网址无法进行提交。举个例子,当用户在网页的html文档中发现了一个图片,但是用户没有权限去从服务器拿回这个图片,这个时候unverifiable应该为true值。      method:method应该是一个字符串,表示HTTP请求方法将被使用(例如“头”)。如果提供,它的值存储在method属性并且通过方法get_method()调用。子类可以通过在类中设置方法属性来指示默认方法。(这个也基本不会用到)    说了这么多枯燥的定义,翻译得我自己都受不了了。    下面继续回到我们的程序中来:     对于我们的程序,只要把握好几个主要的点就好了。首先,我们要构造请求: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/")这一句是很 重要 的,它告诉网站 服务器我们是在哪找到要访问的网页的,比如说你在百度中点击了某个链接跳转到当前访问的页面的,那这个referer就是那个百度中的链接,是一种判断的机制。对于header的构造方法也可以这么做的:
#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中的内容由于是个人主机的信息,所以我就用句号省略了一些,在使用时可以将自己主机的<span style="color: rgb(84, 84, 84); font-family: 'Segoe UI', Tahoma, sans-serif;  white-space: pre-wrap;"><strong>User-Agent放进去</strong></span>
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代理是很容易从网上搜索到的,关于多次访问相关问题我会在以后的博客中解释,在此就不多说了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐