python

urllib库简介:

urllib库的response对象是先创建http,request对象,装载到reques.urlopen里完成http请求。

urllib库的作⽤

  爬⾍的第⼀个步骤是获取⽹页,urllib库是⽤来实现这个功能:想服务器发送请求,得到服务器响应,获取⽹页的内容。

  Python的强⼤在于提供了功能齐全的类库,来帮助我们完成这个请求,通过调⽤urllib库,我们不需要了解请求的数据结构,HTTP,TCP,IP层⽹络传输同学,以及服务器应答原理等。

  我们只需要关⼼以下三点,然后通过⼏⾏调⽤urllib库的代码,就能够获得我们想要的⽹页内容。

请求的URL是什么

传递的参数是什么

如何设置可选的请求头

  urllib库的构成

  在python2中,曾经有urllib和urllib2两个库来实现请求的发送,但⽬前通⽤的python3版本中,两个库的功能已经合并成⼀个库,统⼀为urllib,它是python内置函数,不需要

额外安装即可使⽤。

  urllib的四个模块

  【1】requset:HTTP请求模块,可以⽤来模拟发送请求,只需要传⼊URL及额外参数,就可以模拟浏览器访问⽹页的过程。

  【2】error:异常处理模块,检测请求是否报错,捕捉异常错误,进⾏重试或其他操作,保证程序不会终⽌。

  【3】parse:⼯具模块,提供许多URL处理⽅法,如拆分、解析、合并等。

  【4】robotparser:识别⽹站的robots.txt⽂件,判断哪些⽹站可以爬,哪些⽹站不可以爬,使⽤频率较少。

  发送请求

  urlopen是request模块中的⽅法,⽤于抓取⽹络。

  我们以代码⽰例,我们抓取百度的⽹页

# 调⽤urllib库中的request模块

import urllib.request

# 发送请求获取百度⽹页的响应

response = urllib.request.urlopen("http://www.baidu.com")

返回的是http,response对象,实际上是html属性。使用.read().decode()解码后转化成了str字符串类型,decode解码后中文字符能够显示出来。

# 打印响应内容

# read()是把响应对象内容全部读取出来,读取出来为bytes码

# decode('utf-8')把bytes码解码

print(response.read().decode('utf-8'))

  返回的结果⽐较多,随便截取其中⼀部分,可以看出是百度的⽹页HTML源代码。

  我们只⽤⼏⾏代码,就完成了百度的抓取,并打印了⽹页的源代码,接下来,我们看⼀看我们获得的响应内容response到底是什么?利⽤type()⽅法来输出响应的类型。

print(type(response))

注意:

通常爬取网页,在构造http请求的时候,都需要加上一些额外信息,什么Useragent,cookie等之类的信息,或者添加代理服务器。往往这些都是一些必要的反爬机制。

requests库

简介:

requests库调用是requests.get方法传入url和参数,返回的对象是Response对象,打印出来是显示响应状态码。

通过.text 方法可以返回是unicode 型的数据,一般是在网页的header中定义的编码形式,而content返回的是bytes,二级制型的数据,还有 .json方法也可以返回json字符串。

如果想要提取文本就用text,但是如果你想要提取图片、文件等二进制文件,就要用content,当然decode之后,中文字符也会正常显示。

requests的优势:

Python爬虫时,更建议用requests库。因为requests比urllib更为便捷,requests可以直接构造get,post请求并发起,而urllib.request只能先构造get,post请求,再发起。

response.text和response.content的区别:

response.content :这个是直接从网络上抓取的数据,没有经过任何的编码,所以是一个bytes类型,其实在硬盘上和网络上传输的字符串都是bytes类型

response.text:这个是str的数据类型,是requests库将response.content进行解码的字符串,解码需要指定一个编码方式,requests会根据自己的猜测来判断编码的方式,所以有时候可能会猜测错误,就会导致解码产生乱码,这时候就应该进行手动解码,比如使用response.content.decode('utf-8')

response.text和response.content的区别:

response.content :这个是直接从网络上抓取的数据,没有经过任何的编码,所以是一个bytes类型,其实在硬盘上和网络上传输的字符串都是bytes类型

response.text:这个是str的数据类型,是requests库将response.content进行解码的字符串,解码需要指定一个编码方式,requests会根据自己的猜测来判断编码的方式,所以有时候可能会猜测错误,就会导致解码产生乱码,这时候就应该进行手动解码,比如使用response.content.decode('utf-8')

ASCII

ASCII编码的全称是American Standard Code for Information Interchange,美国信息互换标准代码。这是一种最早的、只用来保存英文文字的编码方式。ASCII编码方式只使用了1个字节(8比特位,可以组合出256种不同的状态)中0~127种组合存储了英文的文字。

GBK

当计算机普及到国内时,因为汉字的常用字就有将近6000个,使用ASCII编码已经完全不能满足使用的需求了。

所以在1981年,国家标准总局发布了GB2312(中国国家标准简体中文字符集),使用2个字节的组合,当两个大于127的字符连在一起时,就表示一个汉字,这样就组合出了7000多个简体字。

后来因为汉字的扩展需求,发布了GBK标准,K是扩展一次汉语拼音的声母。即不再要求第二个字节大于127,只要第一个字节大于127,则表示这是一个汉字的开始。这样共收录了将近22000个汉字和符号。且兼容了GB2312标准。

2005年时修订了GB18030标准,支持了国内少数民族的文字,共收录汉字70000余个。兼容了GBK标准。

Unicode

就如国内定义了GB2312标准一样,当时各个国家都规定了适用于自己语言的一套编码方式。但是这就导致各国相互之间谁也不懂谁的编码,装错字符系统就会导致显示全是乱码。

所以这时ISO(International Standards Organization,国际标准化组织)推出了Unicode标准用以解决这个问题。

Unicode标识以2个字节长度的数字来标识所有字符,除了英文以外的字符全部重新进行了统一编码。

注意Unicode只是一种标准,不是编码方式,给予了每个字符一个16比特位的数字标识,至于这个字符在内存中是由几个字节存储,并不是Unicode标准规定的。

UTF-8

Unicode标准制定后,在很长的一段时间内无法推广,直到互联网的普及,强烈要求出现一种统一的编码方式。然后就诞生了UTF-8,这个使用Unicode标准的编码方式。注意:因此,UTF-8是Unicode标准的一种实现方式。

UTF-8的编码规则很简单,只有两条:

对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和ASCII码是相同的。

对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

下表为编码规则,字母x表示可用编码的位。

Unicode符号范围

UTF-8编码方式(二进制)

0000 0000-0000 007F

0xxxxxxx

0000 0080-0000 07FF

110xxxxx 10xxxxxx

0000 0800-0000 FFFF

1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

以汉字严为例,演示如何实现UTF-8编码:

严的Unicode码是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。

由此可见,汉字的Unicode码和UTF-8编码是不同的,它们之间可以通过规则进行转换。

注意 : 汉字的Unicode码是2个字节,而UTF-8码是3个字节

Python 中的编码方式转换

Python3中的字符序列类型有两种:str和bytes。

bytes对象是一串十六进制格式字符序列,如b'\xe6\x88\x91',前方的b标示这串字符是bytes对象。

将bytes对象通过上述的某种编码方式可以解析为字符串,如将b'\xe6\x88\x91'使用UTF-8编码方式解码得到汉字我,而使用GBK编码方式解码无法得到完整的汉字。

Python3中使用encode()和decode()进行字符与二进制序列之间的转换,可以这样理解:encode(编码)就是把人能看懂的汉字,转换为机器能看懂的二进制序列。decode(解码)就是把人看不懂的二进制序列,转换为汉字。

使用encode()和decode()时,有个encoding参数,默认值为UTF-8,指定了对字符串进行编码或解码时,使用的编码方式。

有关文件的编码方式

前面说了那么多,还没有讲到有关文件的编码方式,而且可能平时使用open()打开文件read()的时候,并没有指定编码方式,也能够正常打印出来文件的内容。

文件的编码方式是在open()是指定的,有个encoding参数,作用和字符串解码一样,如果以非二进制模式(b)打开文件,会默认通过UTF-8方式打开。

所以一份GBK编码的文件,如果不以二进制模式打开、且不设置这个encoding参数,是会报解码错误的(UnicodeDecodeError)。

当然,如果以二进制模式打开文件,再读取到的文本就已经是二进制序列了,不涉及encode问题,而是该以什么解码方式将二进制序列转换为人可以读懂的汉字。

所以对于一个未知编码方式的文件,如何通过代码获取其编码方式,然后转换为我们所期望的编码方式呢?

Python提供了一个第三方库chardet,是char detect的缩写,字符监测。

将二进制序列传入chardet.detect()方法,然后会返回一个字典。该字典有3个键值。

{

    'encoding': 'GB2312',

    'confidence': 0.99,

    'language': 'Chinese'

}

encoding是所识别出来该二进制序列的编码方式。confidence是所识别出来的encoding正确概率(1.0表示100%)。language是该编码方式适用的语言。

可以根据confidence概率决定是否使用encoding编码方式。

伪造请求头

import requests

url = 'https://www.zhihu.com/question/315387406/answer/812734512'

headers = {

    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"

}

response = requests.get(url=url, headers=headers)

print(response.status_code)  # 200

fake_useragent

from fake_useragent import UserAgent

# 实例化 user-agent 对象

ua = UserAgent()

url = 'https://www.zhihu.com/question/315387406/answer/812734512'

headers = {"user-agent": ua.chrome}  # 指定浏览器 user-agent

# 或者可以这样写

# headers = {"user-agent": UserAgent().random}  # 一步到位,随机生成一个 user-agent

response = requests.get(url=url, headers=headers)

print(response.status_code)  # 200

About

什么是fake_useragent?

简单来说,fake_useragent能灵活的帮助我们生成user-agent。

install

pip install fake_useragent

update

pip install -U fake-useragent

查看版本

import fake_useragent

print(fake_useragent.VERSION)  # 0.1.11

Usage

生成指定浏览器的user-agent

import fake_useragent

# 实例化 user-agent 对象

ua = fake_useragent.UserAgent()

# ua.ie

print(ua.ie)  # Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/13.0.782.215)

# ua.msie

print(ua['Internet Explorer'])  # Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727)

# ua.opera

print(ua.opera)  # Opera/9.80 (Windows NT 6.1; U; en-US) Presto/2.7.62 Version/11.01

# ua.chrome

print(ua.chrome)  # Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36

# ua.google

print(ua['google chrome'])  # Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36

# ua.firefox

print(ua.firefox)  # Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1

# ua.ff

print(ua.ff)  # Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/29.0

# ua.safari

print(ua.safari)  # Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5

随机生成user-agent

import fake_useragent

# 实例化 user-agent 对象

ua = fake_useragent.UserAgent()

print(ua.random)  # Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36

print(ua.random)  # Mozilla/5.0 (compatible; MSIE 10.0; Macintosh; Intel Mac OS X 10_7_3; Trident/6.0)

print(ua.random)  # Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36

每次都能随机生成一个UA表示,大大增强了爬虫的真实性。

爬虫程序运行速度是很快,在网站爬取数据时,一个固定 IP 的访问频率就会很高,这不

符合人为操作的标准。所以一些网站会设置一个 IP 访问频率的阈值,如果一个 IP 访问频率

超过这个阈值,说明这个不是人在访问,而是一个爬虫程序。

方法一:设置延时

import time, random

from urllib import request

import chardet

# 获取新浪国内新闻的前十页的新闻数据

base_url = "http://api.roll.news.sina.com.cn/zt_list?

channel=news&cat_1=gnxw&cat_2==gdxw1||=gatxw||=zs�pl||=mtjj&level==1||=2&show_ext=1&show_all=1&show_num=22&tag=1

&format=json&page={}&callback=newsloadercallback&_=1534912318040"

# 构建前十页的 url

target_urls = [ base_url.format(x) for x in range(1, 11)]

# enumerate 方法类似于 jQuery 中的 each 方法,返回迭代对象的索引和迭代对象的迭代数据

for index, url in enumerate(target_urls):

# 如果我们访问的数据是很多页,这时候如果频繁的访问数据

# 可能会触发反爬机制,所以我们可以设置延时,模拟人类行为,如

time.sleep(random.randint(1,10))

# 伪造 UA

req = request.Request(url)

req.add_header("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)")

# 发送请求和获取数据

response = request.urlopen(req)

content = response.read()

# 嗅探编码

charset = chardet.detect(content)['encoding']

html = content.decode(charset)

# 保存数据

with open("html/{}.json".format(index), "w", encoding=charset) as f:

f.write(html)

方法二:代理 IP 地址

爬虫去爬取网站数据的数据的时候,如果单位时间内爬取频次过高,或者其他的原因,被对方识别出来,ip可能会被封禁。这种情况下,通过使用代理ip来解决,作为反爬的策略。

代理ip匿名度:

透明的: 服务器知道了你使用代理ip,也知道你真实的ip

匿名代理: 知道使用了代理ip,不知道真实的ip

高匿代理: 不知道使用了代理ip,也不知道真实的ip(最好的选择)

查看ip 的方法:

在cmd输入命令行:ipconfig(内网的ip——私有的地址)

浏览器访问:ipip.net (外网,上网的ip)

如果设置代理ip,可以通过httpbin.org/ip来查看。不设置代理ip查看则显示我们的真实ip。即上边的外网ip,上网的ip。

代理可以从快代理或豌豆代理处付费获得。(也有可以免费试用的的几个)

查看我们的真实ip的代码:

import requests

url = "http://httpbin.org/ip"

res = requests.get(url)

print(res.text)

复制

输出结果显示真实ip。

设置代理ip:

# 设置一个代理ip,以字典形式呈现,代理ip写在字典值中

proxy = {

    'http': 'xxx.xx.xxx.xxx:xxxx'

}

res = requests.get(url, proxies=proxy)

print(res.text)

复制

输出结果显示设置的代理ip。

选出5个ip为例,从中随机选择一个试用,选5次,且出现异常时避免报错。时间间隔设置为1。

'''

36.6.149.154:xxx

114.233.125.55:xxxxx

117.26.229.24:xxx

122.241.27.24:xxxxx

61.132.171.215:xxx

'''

import random

ips = [('36.6.149.154:xxx'),('114.233.125.55:xxxxx'),('117.26.229.24:xxx'),('122.241.27.24:xxxxx'),('61.132.171.215:xxx'),]

url = 'http://httpbin.org/ip'

for i in range(5):

    try:

        ip = random.choice(ips)

        res = requests.get(url, proxies={'http':ip}, timeout=1)

        print(res.text)

    except Exception as e:

        print('出现异常', e)

你可能感兴趣的:(python)