简单的python爬虫程序

使用 Python 内置的 urllib 库获取网页的 html 信息

获取网页html信息

  1. 获取响应对象向百度(http://www.baidu.com/)发起请求,获取百度首页的 HTML 信息
1
2
3
4
import urllib.request
# urlopen()向URL发请求,返回响应对象
response=urllib.request.urlopen('http://www.baidu.com/')
print(response)

其中 urlopen() 表示打开一个网页地址。

注意:请求的 url 必须带有 http 或者 https 传输协议。

  1. 输出HTML信息

在上述代码的基础上继续编写如下代码:

1
2
3
#提取响应内容
html = response.read().decode('utf-8')
print(html)

通过调用 response 响应对象的 read() 方法提取 HTML 信息,该**方法返回的结果是字节串类型(bytes)**,因此需要使用 decode() 转换为字符串。

urllib常用方法

爬虫库 urllib中的常用方法

  1. urlopen()表示向网站发起请求并获取响应对象

urllib.request.urlopen(url,timeout)
参数说明:
url:表示要爬取数据的 url 地址
timeout:设置等待超时时间,指定时间内未得到响应则抛出超时异常

  1. Request()该方法用于创建请求对象、包装请求头,比如重构 User-Agent(即用户代理,指用户使用的浏览器)使程序更像人类的请求,而非机器。
    urllib.request.Request(url,headers)

参数说明:
url:请求的URL地址。
headers:重构请求头。

  1. html响应对象方法

bytes = response.read() # 返回结果为 bytes 数据类型

string = response.read().decode() # decode()将字节串转换为 string 类型

url = response.geturl() # 返回响应对象的URL地址

code = response.getcode() # 返回请求时的HTTP响应码

  1. 编码解码操作

#字符串转换为字节码
string.encode(“utf-8”)
#字节码转换为字符串
bytes.decode(“utf-8”)

User-Agent(用户代理)

User-Agent 即用户代理,简称“UA”,它是一个特殊字符串头
网站服务器通过识别 “UA”来确定用户所使用的操作系统版本、CPU 类型、浏览器版本等信息。而网站服务器则通过判断 UA 来给客户端发送不同的页面。

网站通过识别请求头中 User-Agent 信息来判断是否是爬虫访问网站。如果是,网站首先对该 IP 进行预警,对其进行重点监控,当发现该 IP 超过规定时间内的访问次数, 将在一段时间内禁止其再次访问网站

爬虫程序UA信息

通过向 HTTP 测试网站(http://httpbin.org/)发送 GET 请求来查看请求头信息,从而获取爬虫程序的 UA

1
2
3
4
5
import urllib.request
#向网站发送get请求
response=urllib.request.urlopen('http://httpbin.org/get')
html = response.read().decode()
print(html)

注意:httpbin.org 这个网站能测试 HTTP 请求和响应的各种信息,比如 cookie、IP、headers 和登录验证等,且支持 GET、POST 等多种方法,对 Web 开发和测试很有帮助。

重构爬虫UA信息

ua信息可以百度得到 一大片然后再复制过来

下面使用urllib.request.Request()方法重构 User-Agent 信息

1
2
3
4
5
6
7
8
9
10
11
12
from urllib import request
url = 'http://httpbin.org/get' #向测试网站发送请求
#重构请求头,伪装成 Mac火狐浏览器访问,可以使用上表中任意浏览器的UA信息
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:65.0) Gecko/20100101 Firefox/65.0'}

#创建请求对象,包装ua信息
req = request.Request(url=url,headers=headers)
#发送请求,获取响应对象
res = request.urlopen(req)
html = res.read().decode('utf-8')
print(html)

上述代码重构了 User-Agent 字符串信息,这样就解决了网站通过识别 User-Agent 来封杀爬虫程序的问题。

重构 UA 也可以通过其他模块实现,比如 requests 模块。

构建User-Agnet代理池

在编写爬虫程序时,一般都会构建一个 User-Agent (用户代理)池,就是把多个浏览器的 UA 信息放进列表中,然后再从中随机选择。构建用户代理池,能够避免总是使用一个 UA 来访问网站,因为短时间内总使用一个 UA 高频率访问的网站,可能会引起网站的警觉,从而封杀掉 IP。

自定义UA代理池

构建代理池的方法也非常简单,当前文件的工作目录中定义一个 py 文件,并将以下 UA 信息以列表的形式粘贴到该文件中再在需要的地方引用这个文件就好

1
2
3
4
5
6
7
8
9
10
11
12
ua_list = [
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
'User-Agent:Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11',
'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0',
' Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1',
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1',
' Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
]

python中有专门的第三方的模块来随机获取浏览器 UA 信息

1
2
3
4
5
6
7
from fake_useragent import UserAgent
#实例化一个对象
ua=UserAgent()
#随机获取一个ie浏览器ua
print(ua.ie)
#随机获取一个火狐浏览器ua
print(ua.firefox)

Python实现编码与解码

Python 的标准库urllib.parse模块中提供了用来编码和解码的方法,分别是 urlencode() 与 unquote() 方法。

urlencode()
该方法实现了对 url 地址的编码操作

unquote()
该方法将编码后的 url 地址进行还原,被称为解码

  1. 编码urlencode()
    打开百度首页,在搜索框中输入“爬虫”,然后点击“百度一下”。当搜索结果显示后,此时地址栏的 URL 信息,如下所示:https://www.baidu.com/s?wd=爬虫&rsv_spt=1&rsv_iqid=0xa3ca348c0001a2ab&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf8&tn=baiduhome_pg&rsv_enter=1&rsv_dl=ib&rsv_sug3=8&rsv_sug1=7&rsv_sug7=101

可以看出 URL 中有很多的查询字符串,而第一个查询字符串就是“wd=爬虫”,其中 wd 表示查询字符串的键,而“爬虫”则代表您输入的值。在网页地址栏中删除多余的查询字符串,最后显示的 URL 如下所示:https://www.baidu.com/s?wd=爬虫

使用搜索修改后的 URL 进行搜索,依然会得到相同页面。因此可知“wd”参数是百度搜索的关键查询参数。

下面编写爬虫程序对 “wd=爬虫”进行编码:

1
2
3
4
5
6
7
from urllib import parse
#构建查询字符串字典
query_string = {'wd' : '爬虫'}
result = parse.urlencode(query_string)
#使用format函数格式化字符串,拼接url地址
url = 'http://www.baidu.com/s?{}'.format(result)
print(url)

编码后的 URL 地址依然可以通过地网页址栏实现搜索功能。

除了使用 urlencode() 方法之外,也可以使用 quote(string) 方法实现编码:

1
2
3
4
5
6
7
from urllib import parse
#注意url的书写格式,和 urlencode存在不同
url = 'http://www.baidu.com/s?wd={}'
word = input('请输入要搜索的内容:')
#quote()只能对字符串进行编码
query_string = parse.quote(word)
print(url.format(query_string))

注意:quote() 只能对字符串编码,而 urlencode() 可以直接对查询字符串字典进行编码。

因此在定义 URL 时,需要注意两者之间的差异。

1
2
3
#urllib.parse
urllib.parse.urlencode({'key':'value'}) #字典
urllib.parse.quote(string) #字符串
  1. 解码unquote(string)解码是对编码后的 URL 进行还原的一种操作
1
2
3
4
from urllib import parse
string = '%E7%88%AC%E8%99%AB'
result = parse.unquote(string)
print(result)
  1. URL地址拼接方式

介绍三种拼接 URL 地址的方法。除了使用 format() 函数外,还可以使用字符串相加,以及字符串占位符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1、字符串相加
baseurl = 'http://www.baidu.com/s?'
params='wd=%E7%88%AC%E8%99%AB'
url = baseurl + params

# 2、字符串格式化(占位符)
params='wd=%E7%88%AC%E8%99%AB'
url = 'http://www.baidu.com/s?%s'% params

# 3、format()方法
url = 'http://www.baidu.com/s?{}'
params='wd=%E7%88%AC%E8%99%AB'
url = url.format(params)

Python re模块用法

在 Python 爬虫过程中,实现网页元素解析的方法有很多,正则解析只是其中之一,常见的还有 BeautifulSoup 和 lxml,它们都支持网页 HTML 元素的解析操作。

re模块常用方法

re.compile()
该方法用来生成正则表达式对象

regex=re.compile(pattern,flags=0)
pattern:正则表达式对象
flags:代表功能标志位,扩展正则表达式的匹配。

re.findall()
根据正则表达式匹配目标字符串内容。

re.findall(pattern,string,flags=0)
该函数的返回值是匹配到的内容列表,如果正则表达式有子组,则只能获取到子组对应的内容
pattern:正则表达式对象
string:目标字符串
flags:代表功能标志位,扩展正则表达式的匹配。

regex.findall()
该函数根据正则表达式对象匹配目标字符串内容。regex.findall(string,pos,endpos)
string 目标字符串。
pos 截取目标字符串的开始匹配位置。
endpos 截取目标字符串的结束匹配位置。

re.split()
该函数使用正则表达式匹配内容,切割目标字符串。返回值是切割后的内容列表。
re.split(pattern,string,flags = 0)
pattern:正则表达式。
string:目标字符串。
flags:功能标志位,扩展正则表达式的匹配。

re.sub
该函数使用一个字符串替换正则表达式匹配到的内容。返回值是替换后的字符串。
re.sub(pattern,replace,string,max,flags = 0)
pattern:正则表达式。
replace:替换的字符串。
string:目标字符串。
max:最多替换几处,默认替换全部,
flags:功能标志位,扩展正则表达式的匹配。

re.search()
匹配目标字符串第一个符合的内容,返回值为匹配的对象。re.search(pattern,string,flags=0)
pattern:正则表达式
string:目标字符串
flags功能标志位

功能标志位的作用是扩展正则表达的匹配功能。常用的 flag

注意:可以同时使用福多个功能标志位,比如 flags=re.I|re.S。

使用贪婪和非贪婪两种模式来匹配 HTML 元素,分别,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import re
html="""
<div><p>www.biancheng.net</p></div>
<div><p>编程帮</p></div>
"""
#贪婪匹配,re.S可以匹配换行符
pattern=re.compile('<div><p>.*</p></div>',re.S)
re_list=pattern.findall(html)
print(re_list)

#非贪婪模式匹配,re.S可以匹配换行符
pattern=re.compile('<div><p>.*?</p></div>',re.S)
re_list=pattern.findall(html)
print(re_list)
正则表达式分组

通过正则表达式分组可以从匹配的信息中提取出想要的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#正则表达式分组
website="编程帮 www.biancheng.net"

#注意此时正则表达式的 "." 需要转义因此使用 \.
pattern_1=re.compile('\w+\s+\w+\.\w+\.\w+')
print(pattern_1.findall(website))

#提取匹配信息的第一项
pattern_2=re.compile('(\w+)\s+\w+\.\w+\.\w+')
print(pattern_2.findall(website))

#有两个及以上的()则以元组形式显示
pattern_3=re.compile('(\w+)\s+(\w+\.\w+\.\w+)')
print(pattern_3.findall(website))

正则表达式分组是提取信息的常用方式。当需要哪个特定信息的时候,就可以通过分组(也就是加括号)的方式获得。

网页信息提取

从下面的 HTML 代码中使用 re 模块提取出两部影片的名称和主演信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
html="""
<div class="movie-item-info">
<p class="name">
<a title="你好,李焕英">你好,李焕英</a>
</p>
<p class="star">
主演:贾玲,张小斐,沈腾
</p>
</div>

<div class="movie-item-info">
<p class="name">
<a title="刺杀,小说家">刺杀,小说家</a>
</p>
<p class="star">
主演:雷佳音,杨幂,董子健,于和伟
</p>
</div>
"""
# 寻找HTML规律,书写正则表达式,使用正则表达式分组提取信息
pattern=re.compile(r'<div.*?<a title="(.*?)".*?star">(.*?)</p.*?div>',re.S)
r_list=pattern.findall(html)
print(r_list)
# 整理数据格式并输出
if r_list:
for r_info in r_list:
print("影片名称:",r_info[0])
print("影片主演:",r_info[1].strip())
print(20*"*")

这里给一些博主自己使用正则表达式的经验:
<div.?<a title=”(.*?)”.*?star”>(.?)</p.*?div>
比如说上面这个正则表达式
他就只能匹配下面这种类型里面的信息

你好,李焕英

主演:贾玲,张小斐,沈腾

刺杀,小说家

主演:雷佳音,杨幂,董子健,于和伟

你如果需要匹配的对象(注意仔细看博主删减的那部分)是这样的(如下)
他就只能匹配到,第一部影片的信息第二部影片的信息好像匹配不到
而遇到这种情况 就使用xlme来匹配更好

你好,李焕英

主演:贾玲,张小斐,沈腾

刺杀,小说家

主演:雷佳音,杨幂,董子健,于和伟

Python csv模块(读写文件)

CSV 文件又称为逗号分隔值文件,是一种通用的、相对简单的文件格式,用以存储表格数据,包括数字或者字符。CSV 是电子表格和数据库中最常见的输入、输出文件格式

通过爬虫将数据抓取的下来,然后把数据保存在文件,或者数据库中,这个过程称为数据的持久化存储

CSV文件写入
  1. csv.writer()
    csv 模块中的 writer 类可用于读写序列化的数据
    其语法格式如下:writer(csvfile, dialect=’excel’, **fmtparams)

csvfile:必须是支持迭代(Iterator)的对象,可以是文件(file)对象或者列表(list)对象。
dialect:编码风格,默认为 excel 的风格,也就是使用逗号,分隔。
fmtparam:格式化参数,用来覆盖之前

dialect 对象指定的编码风格。

1
2
3
4
5
6
7
8
9
import csv
# 操作文件对象时,需要添加newline参数逐行写入,否则会出现空行现象
with open('eggs.csv', 'w', newline='') as csvfile:
# delimiter 指定分隔符,默认为逗号,这里指定为空格
# quotechar 表示引用符
# writerow 单行写入,列表格式传入数据
spamwriter = csv.writer(csvfile, delimiter=' ',quotechar='|')
spamwriter.writerow(['www.biancheng.net'] * 5 + ['how are you'])
spamwriter.writerow(['hello world', 'web site', 'www.biancheng.net'])

其中,quotechar 是引用符,当一段话中出现分隔符的时候,用引用符将这句话括起来,以能排除歧义。

如果想同时写入多行数据,需要使用 writerrows() 方法

1
2
3
4
5
import csv
with open('aggs.csv', 'w', newline='') as f:
writer = csv.writer(f)
# 注意传入数据的格式为列表元组格式
writer.writerows([('hello','world'), ('I','love','you')])
  1. csv.DictWriter()当然也可使用 DictWriter 类以字典的形式读写数据
1
2
3
4
5
6
7
8
9
10
11
import csv
with open('names.csv', 'w', newline='') as csvfile:
#构建字段名称,也就是key
fieldnames = ['first_name', 'last_name']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
# 写入字段名,当做表头
writer.writeheader()
# 多行写入
writer.writerows([{'first_name': 'Baked', 'last_name': 'Beans'},{'first_name': 'Lovely', 'last_name': 'Spam'}])
# 单行写入
writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})
CSV文件读取
  1. csv,reader()
    csv 模块中的 reader 类和 DictReader 类用于读取文件中的数据

reader() :
csv.reader(csvfile, dialect=’excel’, **fmtparams)

1
2
3
4
5
import csv
with open('eggs.csv', 'r', newline='') as csvfile:
spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
for row in spamreader:
print(', '.join(row))
  1. csv.DictReader() :
1
2
3
4
5
import csv
with open('names.csv', newline='') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
print(row['first_name'], row['last_name'])

Python Requests库的使用

Python 提供了多个用来编写爬虫程序的库,除了 urllib 库之外,还有一个很重的 Requests 库

Requests 库是在 urllib 的基础上开发而来,它使用 Python 语言编写,并且采用了 Apache2 Licensed(一种开源协议)的 HTTP 库。

与 urllib 相比,Requests 更加方便、快捷,因此在编写爬虫程序时 Requests 库使用较多。

常用请求方法
  1. requests.get()该方法用于 GET 请求,表示向网站发起请求,获取页面响应对象。

res = requests.get(url,headers=headers,params,timeout)
参数说明如下:url:要抓取的 url 地址。
headers:用于包装请求头信息。
params:请求时携带的查询字符串参数。
timeout:超时时间,超过时间会抛出异常。

1
2
3
4
import requests
url = 'http://baidu.com'
response = requests.get(url)
print(response)

获取带查询字符串参数的响应对象:

1
2
3
4
5
6
7
8
9
10
11
import requests
data = {
'name': '编程帮',
'url': "www.biancheng.net"
}

response = requests.get('http://httpbin.org/get', params=data)
#直接拼接参数也可以
#response = requests.get(http://httpbin.org/get?name=gemey&age=22)
#调用响应对象text属性,获取文本信息
print(response.text)
  1. requests.post()

该方法用于 POST 请求,先由用户向目标 url 提交数据,然后服务器返回一个 HttpResponse 响应对象,语法如下:
response=requests.post(url,data={请求体的字典})

1
2
3
4
5
6
7
8
9
10
11
import requests
#百度翻译
url = 'https://fanyi.baidu.com'
#post请求体携带的参数,可通过开发者调试工具查看
#查看步骤:NetWork选项->Headers选项->Form Data
data = {'from': 'zh',
'to': 'en',
'query': '编程帮www.biancheng.net你好'
}
response = requests.post(url, data=data)
print(response)
常见对象属性

当我们使用 Requests 模块向一个 URL 发起请求后会返回一个 HttpResponse 响应对象,该对象具有以下常用属性:

常用属性说明

encoding查看或者指定响应字符编码

status_code返回HTTP响应码

url查看请求的 url 地址

headers查看请求头信息

cookies查看cookies 信息

text以字符串形式输出

content以字节流形式输出,若要保存下载图片需使用该属性。

1
2
3
4
5
6
7
8
9
10
import requests
response = requests.get('http://www.baidu.com')
print(response.encoding)
response.encoding="utf-8" #更改为utf-8编码
print(response.status_code) # 打印状态码
print(response.url) # 打印请求url
print(response.headers) # 打印头信息
print(response.cookies) # 打印cookie信息
print(response.text) #以字符串形式打印网页源码
print(response.content) #以字节流形式打印
Requests库常用方法及参数介绍

Requests 库中定义了七个常用的请求方法,这些方法各自有着不同的作用,在这些请求方法中 requests.get() 与 requests.post() 方法最为常用。
请求方法如下所示:

requests.request()
构造一个请求对象,该方法是实现以下各个方法的基础。

requests.get()
获取HTML网页的主要方法,对应于 HTTP 的 GET 方法。

requests.head()
获取HTML网页头信息的方法,对应于 HTTP 的 HEAD 方法。

requests.post()
获取 HTML 网页提交 POST请求方法,对应于 HTTP 的 POST。

requests.put()
获取HTML网页提交PUT请求方法,对应于 HTTP 的 PUT。

requests.patch()
获取HTML网页提交局部修改请求,对应于 HTTP 的 PATCH。

requests.delete()
获取HTML页面提交删除请求,对应于 HTTP 的 DELETE。

上述方法都提供了相同的参数,其中某些参数已经使用过,比如headers和params,前者用来构造请求头,后者用来构建查询字符串。这些参数对于编写爬虫程序有着至关重要的作用

SSL认证-verify参数

SSL 证书是数字证书的一种,类似于驾驶证、护照和营业执照。因为配置在服务器上,也称为 SSL 服务器证书。SSL 证书遵守 SSL 协议,由受信任的数字证书颁发机构 CA(电子认证服务)颁发。 SSL 具有服务器身份验证和数据传输加密功能。

verify参数的作用是检查 SSL 证书认证,参数的默认值为 True,如果设置为 False 则表示不检查 SSL证书,此参数适用于没有经过 CA 机构认证的 HTTPS 类型的网站。

1
2
3
4
5
6
response = requests.get(
url=url,
params=params,
headers=headers,
verify=False
)

代理IP-proxies参数

一些网站为了限制爬虫从而设置了很多反爬策略,其中一项就是针对 IP 地址设置的。
比如,访问网站超过规定次数导致流量异常,或者某个时间段内频繁地更换浏览器访问,存在上述行为的 IP 极有可能被网站封杀掉。

代理 IP 就是解决上述问题的,它突破了 IP 地址的访问限制,隐藏了本地网络的真实 IP,而使用第三方 IP 代替自己去访问网站。

  1. 代理IP池通过构建代理 IP 池可以让你编写的爬虫程序更加稳定,从 IP 池中随机选择一个 IP 去访问网站,而不使用固定的真实 IP。总之将爬虫程序伪装的越像人,它就越不容易被网站封杀。当然代理 IP 也不是完全不能被察觉,通过端口探测技等术识仍然可以辨别。

  2. proxies参数Requests 提供了一个代理 IP 参数 proxies,该参数的语法结构如下:

proxies = {
‘协议类型(http/https)’:’协议类型://IP地址:端口号’
}

下面构建了两个协议版本的代理 IP,示例如下:

proxies = {
‘http’:’http://IP:端口号',
‘https’:’https://IP:端口号'
}

  1. 代理IP使用
  2. 下面通过简单演示如何使用proxies参数,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
import requests
url = 'http://httpbin.org/get'
headers = {
'User-Agent':'Mozilla/5.0'
}
# 网上找的免费代理ip
proxies = {
'http':'http://191.231.62.142:8000',
'https':'https://191.231.62.142:8000'
}
html = requests.get(url,proxies=proxies,headers=headers,timeout=5).text
print(html)

由于上述示例使用的是免费代理 IP,因此其质量、稳定性较差,可能会随时失效。如果想构建一个稳定的代理 IP 池,就需要花费成本。

  1. 付费代理IP网上有许多提供代理 IP 服务的网 站,比如快代理、代理精灵、齐云代理等。这些网站也提供了相关文档说明,以及 API 接口,爬虫程序通过访问 API 接口,就可以构建自己的代理 IP 池。付费代理 IP 按照资源类型可划分为:开发代理、私密代理、隧道代理、独享代理,其中最常使用的是开放代理与私密代理。
  2. 开放代理:开放代理是从公网收集的代理服务器,具有 IP 数量大,使用成本低的特点,全年超过 80% 的时间都能有 3000 个以上的代理 IP 可供提取使用。
    私密代理:私密代理是基于云主机构建的高品质代理服务器,为您提供高速、可信赖的网络代理服务。私密代理每天可用 IP 数量超过 20 万个,可用率在 95 %以上,1 次可提取 IP 数量超过 700 个,可以为爬虫业务提供强大的助力。付费代理的收费标准根据 IP 使用的时间长短,以及 IP 的质量高低,从几元到几百元不等。89 免费代理(http://www.89ip.cn/)是一个专门提供免费代理 IP 的网站,不过想找到一个质量较高的免费代理好比大海捞针。

用户认证-auth参数
Requests 提供了一个auth参数,该参数的支持用户认证功能,也就是适合那些需要验证用户名、密码的网站。auth 的参数形式是一个元组

格式:
auth = (‘username’,’password’)

Xpath

在编写爬虫程序的过程中提取信息是非常重要的环节,但是有时使用正则表达式无法匹配到想要的信息,或者书写起来非常麻烦,此时就需要用另外一种数据解析方法, Xpath 表达式。

Xpath表达式

XPath(全称:XML Path Language)即 XML 路径语言,它是一门在 XML 文档中查找信息的语言,最初被用来搜寻 XML 文档,同时它也适用于搜索 HTML 文档。因此,在爬虫过程中可以使用 XPath 来提取相应的数据。

可以将 Xpath 理解为在XML/HTML文档中检索、匹配元素节点的工具。

Xpath 使用路径表达式来选取XML/HTML文档中的节点或者节点集。

Xpath 的功能十分强大,它除了提供了简洁的路径表达式外,还提供了100 多个内建函数,包括了处理字符串、数值、日期以及时间的函数。

因此 Xpath 路径表达式几乎可以匹配所有的元素节点。

Python 第三方解析库 lxml 对 Xpath 路径表达式提供了良好的支持,能够解析 XML 与 HTML 文档

Xpath节点

XPath 提供了多种类型的节点,常用的节点有:元素、属性、文本、注释以及文档节点。

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<website>

<site>
<title lang="zh-CN">website name</title>
<name>编程帮</name>
<year>2010</year>
<address>www.biancheng.net</address>
</site>

</website>

上面的 XML 文档中的节点例子:

(文档节点)
(元素节点)
lang=”zh-CN” (属性节点)

节点关系XML 文档的节点关系和 HTML 文档相似,同样有父、子、同代、先辈、后代节点。

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<website>

<site>
<title lang="zh-CN">website name</title>
<name>编程帮</name>
<year>2010</year>
<address>www.biancheng.net</address>
</site>

</website>

上述示例分析后,会得到如下结果:title name year address 都是 site 的子节点
site 是 title name year address 父节点
title name year address 属于同代节点
title 元素的先辈节点是 site website
website 的后代节点是 site title name year address

Xpath基本语法
  1. 基本语法使用
    Xpath 使用路径表达式在文档中选取节点,下表列出了常用的表达式规则:

表达式描述
node_name
选取此节点的所有子节点。

/
绝对路径匹配,从根节点选取。

//
相对路径匹配,从所有节点中查找当前选择的节点,包括子节点和后代节点,其第一个 / 表示根节点。

.
取当前节点。

..
选取当前节点的父节点。

@
选取属性值,通过属性值选取数据。
常用元素属性有 @id 、@name、@type、@class、@tittle、@href。

下面讲解 Xpath 表达式的基本应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ul class="BookList">
<li class="book1" id="book_01" href="http://www.biancheng.net/">
<p class="name">c语言小白变怪兽</p>
<p class="model">纸质书</p>
<p class="price">80元</p>
<p class="color">红蓝色封装</p>
</li>

<li class="book2" id="book_02" href="http://www.biancheng.net/">
<p class="name">Python入门到精通</p>
<p class="model">电子书</p>
<p class="price">45元</p>
<p class="color">蓝绿色封装</p>
</li>
</ul>

路径表达式以及相应的匹配内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
xpath表达式://li

匹配内容:
c语言小白变怪兽
纸质书
80元
红蓝色封装


Python入门到精通
电子书
45元
蓝绿色封装

xpath表达式://li/p[@class="name"]
匹配内容:
c语言小白变怪兽
Python入门到精通


xpath表达式://li/p[@class="model"]
匹配内容:
纸质书
电子书

xpath表达式://ul/li/@href
匹配内容:
http://www.biancheng.net/
http://www.biancheng.net/

xpath表达式://ul/li
匹配内容:
c语言小白变怪兽
纸质书
80元
红蓝色封装

Python入门到精通
电子书
45元
蓝绿色封装

注意:当需要查找某个特定的节点或者选取节点中包含的指定值时需要使用[]方括号。如下所示:

1
2
xpath表达式://ul/li[@class="book2"]/p[@class="price"]
匹配结果:45元
  1. xpath通配符Xpath 表达式的通配符可以用来选取未知的节点元素,基本语法如下:
    • 匹配任意元素节点

@*
匹配任意属性节点

node()
匹配任意类型的节点

1
2
3
4
5
6
7
8
9
10
11
xpath表达式://li/*

匹配内容:
c语言小白变怪兽
纸质书
80元
红蓝色封装
Python入门到精通
电子书
45元
蓝绿色封装
  1. 多路径匹配多个 Xpath 路径表达式可以同时使用,其语法如下:
    xpath表达式1 | xpath表达式2 | xpath表达式3
1
2
3
4
5
6
表达式://ul/li[@class="book2"]/p[@class="price"]|//ul/li/@href

匹配内容:
45元
http://www.biancheng.net/
http://www.biancheng.net/

Xpath内建函数Xpath 提供 100 多个内建函数,这些函数给我们提供了很多便利,比如实现文本匹配、模糊匹配、以及位置匹配等,下面介绍几个常用的内建函数。

Python lxml库的使用

lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 Xpath 表达式提供了良好的支持,因此能够了高效地解析 HTML/XML 文档。

lxml使用流程

lxml 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档

lxml 库的使用流程:

  1. 导入模块
    from lxml import etree

  2. 创建解析对象调用 etree 模块的 HTML() 方法来创建 HTML 解析对象。parse_html = etree.HTML(html)

HTML() 方法能够将 HTML 标签字符串解析为 HTML 文件,该方法可以自动修正 HTML 文本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from lxml import etree
html_str = '''
<div>
<ul>
<li class="item1"><a href="link1.html">Python</a></li>
<li class="item2"><a href="link2.html">Java</a></li>
<li class="site1"><a href="c.biancheng.net">C语言中文网</a>
<li class="site2"><a href="www.baidu.com">百度</a></li>
<li class="site3"><a href="www.jd.com">京东</a></li>
</ul>
</div>
'''
html = etree.HTML(html_str)

# tostring()将标签元素转换为字符串输出,注意:result为字节类型
result = etree.tostring(html)

print(result.decode('utf-8'))

上述 HTML 字符串存在缺少标签的情况,比如“C语言中文网”缺少一个 闭合标签,当使用了 HTML() 方法后,会将其自动转换为符合规范的 HTML 文档格式

  1. 调用xpath表达式最后使用第二步创建的解析对象调用 xpath() 方法,完成数据的提取,如下所示:
1
r_list = parse_html.xpath('xpath表达式')

lxml库

数据提取下面通过一段 HTML 代码实例演示如何使用 lxml 库提取想要的数据。HTML 代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="wrapper">
<a href="www.biancheng.net/product/" id="site">website product</a>
<ul id="sitename">
<li><a href="http://www.biancheng.net/" title="编程帮">编程</a></li>
<li><a href="http://world.sina.com/" title="新浪娱乐">微博</a></li>
<li><a href="http://www.baidu.com" title="百度">百度贴吧</a></li>
<li><a href="http://www.taobao.com" title="淘宝">天猫淘宝</a></li>
<li><a href="http://www.jd.com/" title="京东">京东购物</a></li>
<li><a href="http://c.bianchneg.net/" title="C语言中文网">编程</a></li>
<li><a href="http://www.360.com" title="360科技">安全卫士</a></li>
<li><a href="http://www.bytesjump.com/" title=字节">视频娱乐</a></li>
<li><a href="http://bzhan.com/" title="b站">年轻娱乐</a></li>
<li><a href="http://hao123.com/" title="浏览器">搜索引擎</a></li>
</ul>
</div>
  1. 提取所有a标签内的文本信息
1
2
3
4
5
6
7
8
9
from lxml import etree
# 创建解析对象
parse_html=etree.HTML(html)
# 书写xpath表达式,提取文本最终使用text()
xpath_bds='//a/text()'
# 提取文本数据,以列表形式输出
r_list=parse_html.xpath(xpath_bds)
# 打印数据列表
print(r_list)
  1. 获取所有href的属性值
1
2
3
4
5
6
7
8
9
from lxml import etree
# 创建解析对象
parse_html=etree.HTML(html)
# 书写xpath表达式,提取文本最终使用text()
xpath_bds='//a/@href'
# 提取文本数据,以列表形式输出
r_list=parse_html.xpath(xpath_bds)
# 打印数据列表
print(r_list)
  1. 不匹配href=” www.biancheng.net/priduct"
1
2
3
4
5
6
7
8
9
from lxml import etree
# 创建解析对象
parse_html=etree.HTML(html)
# 书写xpath表达式,提取文本最终使用text()
xpath_bds='//a/@href'
# 提取文本数据,以列表形式输出
xpath_bds='//ul[@id="sitename"]/li/a/@href'
# 打印数据列表
print(r_list)

Python json模块常用方法

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,遵循欧洲计算机协会制定的 JavaScript 规范(简称 ECMAScript)。

它常被作为网络、程序之间传递信息的标准语言,比如客户端与服务器之间信息交互就是以 JSON 格式传递的。

简单地说,JSON 可以将 JavaScript 对象表示的一组数据转换为字符串格式,以便于在网络、程序间传输这个字符串。

并且在需要的时候,您还可以将它转换为编程语言所支持的数据格式。本节主要介绍如何实现 JSON 数据与 Python 数据类型间的相互转换。

Python 语言内置了专门处理 JOSN 数据的模块 —— jons 模块,通过该模块就可以完成 JSON 与 Python 两种数据格式的相互转换。

json.dump()

它可以将 Python 对象(字典、列表等)转换为 json 字符串,并将转换后的数据写入到 json 格式的文件中 ,因此该方法必须操作文件流对象。

比如当使用爬虫程序完成数据抓取后,有时需要将数据保存为 json 格式,此时就用到了 json.dump() 方法,语法格式如下:

json.dump(object,f,inden=0,ensure_ascii=False)

object:Python 数据对象,比如字典,列表等

f:文件流对象,即文件句柄。

indent:格式化存储数据,使 JSON 字符串更易阅读。

ensure_ascii:是否使用 ascii 编码,当数据中出现中文的时候,需要将其设置为 False。

1
2
3
4
5
import json

ditc_info={"name" : "c语言中文网","PV" : "50万","UV" : "20万","create_time" : "2010年"}
with open("web.josn","a") as f:
json.dump(ditc_info,f,ensure_ascii=False)

也可以将 Python 列表转换成 JSON 字符串,并保存至 json 文件中,如下所示:

1
2
3
4
5
6
7
8
9
import json

item_list = []
item = {'website': 'C语言中文网', 'url': "c.biancheng.net"}
for k,v in item.items():
item_list.append(v)

with open('info_web.json', 'a') as f:
json.dump(item_list, f, ensure_ascii=False)

json.load()

该方法用于操作文件流对象,不过它与 dump() 恰好相反,它表示从 json 文件中读取 JSON 字符串,并将读取内容转换为 Python 对象

1
2
3
4
5
6
7
8
9
import json

site = {'name':'c语言中文网',"url":"c.biancheng.net"}
filename = 'website.json'
with open (filename,'w') as f:
json.dump(site,f,ensure_ascii=False)
with open (filename,'r') as f:
print(json.load(f))

json.loads()

该方法可以将 json 格式的字符串转换成 Python 对象(比如列表、字典、元组、整型以及浮点型),其中最常用的是转换为字典类型。

1
2
3
4
5
6
# coding:utf8
import json
#JOSN字符串
website_info='{"name" : "c语言中文网","PV" : "50万","UV" : "20万","create_time" : "2010年"}'
py_dict=json.loads(website_info)
print("python字典数据格式:%s;数据类型:%s"% (py_dict,type(py_dict)))

注意:上述示例中 JSON 字符串看上去和 Python 字典非常相似,但是其本质不同,JOSN 是字符串类型,而 Python 字典是 dict 类型。

json.dumps()
该方法可以将 Python 对象转换成 JSON 字符串

1
2
3
4
5
6
7
import json
#python字典
item = {'website': 'C语言中文网', 'rank': 1}
# json.dumps之后
item = json.dumps(item,ensure_ascii=False)
print('转换之后的数据类型为:',type(item))
print(item)

方法作用json.dumps()将 Python 对象转换成 JSON 字符串。json.loads()将 JSON 字符串转换成 Python 对象。json.dump()将 Python 中的对象转化成 JSON 字符串储存到文件中。json.load()将文件中的 JSON 字符串转化成 Python 对象提取出来。

综上所述 json.load() 与 json.dump() 操作的是文件流对象,实现了 json 文件的读写操作,而 json.loads() 与 json.dumps() 操作的是 Python 对象或者 JOSN 字符串。