网络爬虫2-爬虫初步
本章主要介绍初步的python爬虫方法
包括处理url参数,构建请求对象,模拟各种请求方式,handler处理器,opener和代理等
url lib.parse()
主要的方法用于处理参数或者url:
url.parse.quote()
url中不能使用中文,使用的是url编码也可以说是百分号编码
url中只能有字母、数字、下划线、冒号 // ? =等
1
2
3
4
5
6
7
8
9import urllib.parse
url = 'https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=0&rsv_idx=1&tn=baidu&wd=%E5%B0%8F%E5%8F%AF%E7%88%B1&rsv_pq=96be106200031fd5&rsv_t=7e37zpPVn6Se%2FFpX446ehNfmIYvVrtgj1SOOkWgO48bW4gpLGLIloR6C3%2Fk&rqlang=cn&rsv_enter=1&rsv_sug3=11&rsv_sug1=3&rsv_sug7=101&rsv_sug2=0&prefixsug=%25E5%25B0%258F%25E5%258F%25AF%25E7%2588%25B1&rsp=3&inputT=3801&rsv_sug4=3801'
string = urllib.parse.quote(url)
string_reverse = urllib.parse.unquote(string)
print(string)
print(string_reverse)其中对url编码是对所有的(包括=号等都进行了编码),编码后不可访问,应该对中文参数单独进行编码;解码后可访问
urllib.parse.urlencode()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17url = 'https://www.baidu.com/s?'
data = {
'ie': 'utf8',
'wd': 'little'
}
# 将data拼接到url后面,组成完整的url
# 方法:遍历这个字典,拼接为制定格式
lt = []
for k, v in data.items():
value = k + '=' + v
lt.append(value)
# 将lt用&符号拼接
query_string = '&'.join(lt)
# 拼接url
url += query_string
print(url)以上的办法可以将data中的参数处理为url中可以携带的参数,但我们有更加简便的写法,直接使用urllib.parse.urlencode(data)方法
1
2
3
4# 使用urlencode()
query_string = urllib.parse.urlencode(data)
url += query_string
print(url)
也可以用url编码工具
构建请求对象
不同的浏览器UA都是不同的,网站服务器根据UA给予不同的响应
我们需要伪装UA进行请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import urllib.request
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
url = 'http://www.baidu.com/'
# 如何定制UA
# 在这个头部不仅可以定制ua,还可以定制其他的请求头部,一般只需要定制ua
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36'
}
# 构建请求对象
request = urllib.request.Request(url=url, headers=headers)
# 发送请求,直接打开这个请求对象即可
response = urllib.request.urlopen(request)模拟各种请求方式
get
模拟发送带参数的get
例子:输入搜索国家,单独保存为txt文件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
35import urllib.request
import urllib.parse
# 让用户输入搜索关键字
keyword = input('请输入要搜索的关键字:')
url = 'https://www.baidu.com/s?'
# get参数
data = {
'ie': 'utf8',
'wd': keyword,
}
query_string = urllib.parse.urlencode(data)
url += query_string
# 向url发送请求,得到响应
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.3',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
# 拼接文件名字
filename = keyword + '.html'
# 写入到文件中
with open(filename, 'wb') as fp:
fp.write(response.read())post
例子:百度翻译
调用百度翻译的sug接口,进行简单翻译1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import urllib.request
import urllib.parse
url = 'http://fanyi.baidu.com/sug'
# 将表单数据写成一个字典
formdata = {
'kw': 'baby'
}
# 将formdata单独处理一下
formdata = urllib.parse.urlencode(formdata).encode('utf8')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.3',
}
# 构建请求对象
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request, data=formdata)
print(response.read().decode('utf8'))
调用百度翻译的接口,获取复杂一些的翻译
在头部中除了CA还需要发送多个头部参数,可以得到单个针对性单词的翻译,但如果需要根据单词来查询需要破解百度翻译的加密sign和token
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
39import urllib.request
import urllib.parse
# 加密的接口,如果想要得到,需要破解
url = 'http://fanyi.baidu.com/v2transapi'
word = 'wolf'
# 表单数据
formdata = {
'from': 'en',
'to': 'zh',
'query': word,
'transtype': 'realtime',
'simple_means_flag': '3',
'sign': '275695.55262',
'token': '268ca3a468d99f5aac3a179efad0ab28',
}
# 处理表单数据
formdata = urllib.parse.urlencode(formdata).encode('utf8')
headers = {
# 'Accept': '*/*',
# 将其注释掉,索要完整的格式
# 'Accept-Encoding': 'gzip, deflate',
# 'Accept-Language': 'zh-CN,zh;q=0.9',
# 'Connection': 'keep-alive',
# 将其注释掉,让其自动计算即可
# 'Content-Length': '120',
# 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'BAIDUID=55279ECD6DDA84C66A41BA7CC1E6840E:FG=1; PSTM=1533627007; BIDUPSID=6F6C332F8A0E3C9949BD5D9F884F1FFB; PSINO=3; BDRCVFR[Y1-7gJ950Fn]=jCHWiyEa0lYpAN8n1msQhPEUf; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=1465_26963_26432_21099_26350_26925_22157; locale=zh; to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1533694190; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1533694190; from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D',
'Host': 'fanyi.baidu.com',
'Origin': 'http://fanyi.baidu.com',
'Referer': 'http://fanyi.baidu.com/?aldtype=16047',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request, data=formdata)
print(response.read().decode('utf-8'))可能出现的错误有:
{“error”:997,”from”:”en”,”to”:”zh”,”query”:”(‘wolf’,)”}:错误原因在于你没有模拟浏览器发送请求,请求头不一样,请求头有12个参数
有一些请求头参数不是必须的,比如以下两个,去掉就可以了
‘Accept-Encoding’: ‘gzip, deflate’:需要解压缩才能读取,所以直接去掉
‘Content-Length’: ‘120’: 浏览器计算长度,去掉,让其自动计算
加密的接口,需要破解
ajax-get
例子:豆瓣电影排行榜
获取豆瓣电影排行榜请求的ajax-get的url
让用户输入需要第几页的数据
通过分析我们可以得知,在豆瓣电影排行榜的url中的参数有start&limit两个,其中start是偏移量,limit是每一页显示的数量,每一页显示的睡昂是固定的10条,而偏移量start是等于(n-1)*10开始。
1 | import urllib.request |
ajax-post
在肯德基的官网上爬去城市门店的分布,其为post类型的ajax请求,我们下面模拟浏览器对官网发起请求,获取数据,而请求的url可以在下图中看到
例子:肯德基

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import urllib.request
import urllib.parse
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
city = input('请输入要搜索的城市:')
data = {
'cname': city,
'pid': '',
'pageIndex': '1',
'pageSize': '10'
}
data = urllib.parse.urlencode(data).encode('utf8')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request, data=data)
print(response.read().decode('utf8'))
URLError:断网或者主机不存在的时候会被触发
为了使代码更加健壮,应该对可能出现异常的代码进行异常捕获,使用try,Exception进行捕获
1
2
3
4
5
6
7url = 'http://www.maodan.com/'
try:
response = urllib.request.urlopen(url)
except Exception as e:
print(e)
print('不影响后面的代码')Exception: 是官方的异常基类,可以捕获所有的异常
但是如果想精确捕获:需要指定具体的异常,如果指定错误当然就无法捕获了
HTTPEroor:文件不存在
HTTPEroor是URLError的子类,如果多个except同时捕获,为了能够精确捕获应该把父类写在最后
复杂get
例子:百度贴吧
需求描述:输入贴吧名,输入需要爬取的气势和结束页码,以贴吧名字创建文件夹,将每一页的内容全部保存下来到第n页.html中
我们需要有下载功能的函数,保存功能的函数两个函数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
43
44
45
46
47
48
49
50
51
52
53
54
55
56import urllib.request
import urllib.parse
import os
import time
def main():
name = input('请输入要爬取的贴吧的名字:')
start_page = int(input('请输入要爬取的起始页码:'))
end_page = int(input('请输入要爬取的结束页码:'))
url = 'https://tieba.baidu.com/f?'
for page in range(start_page, end_page + 1):
print('正在爬取第%s页......' % page)
# 根据url和page拼接指定页码的url
request = handle_request(page, baming, url)
# 根据请求对象发送请求得到响应写入到指定的文件中
down_load(request, baming, page)
print('结束爬取第%s页' % page)
time.sleep(3)
# 模拟发起请求,保存数据到指定的地址
def down_load(request, name, page):
response = urllib.request.urlopen(request)
# 通过代码创建指定的文件夹
dirname = name
# 判断不存在的时候创建
if not os.path.exists(dirname):
os.mkdir(dirname)
# 文件的名字
filename = '第%s页.html' % page
# 得到文件的路径
filepath = os.path.join(dirname, filename)
# 将内容直接写入到filepath中
with open(filepath, 'wb') as fp:
fp.write(response.read())
# 构建url和请求对象
def handle_request(page, name, url):
pn = (page-1) * 50
# 拼接url
data = {
'kw': name,
'ie': 'utf8',
'pn': pn
}
url += urllib.parse.urlencode(data)
# print(url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
}
# 构建请求对象
request = urllib.request.Request(url, headers=headers)
return request
if __name__ == '__main__':
main()Handler处理器,自定义Opener
urlopen不能自己定制头部,引入自己定制的头部
为了解决代理和cookie这些更加高级的功能,而引入handler代理
首先我们来熟悉一下这个,使用它请求一个最简单的功能,高级功能的步骤是一摸一样的
1
2
3
4
5
6
7
8
9
10
11import urllib.request
url = 'http://www.baidu.com/'
# 创建handler
handler = urllib.request.HTTPHandler()
# 根据handler创建opener
opener = urllib.request.build_opener(handler)
# 发送请求的时候不要使用urlopen使用opener.open发送
response = opener.open(url)
print(response.read().decode('utf8'))代理
访问不能过于频繁,需要休眠7-10秒左右
可以设置多个代理,交替请求
代理服务器:快代理,西祠代理,芝麻代理,阿布云代理等
代理是需要购买,分为高匿代理,透明代理,普通代理
代理有了ip和端口号就能使用
验证代理是否可用,可以用程序,软件(花刺代理验证)等方式
- 浏览器如何设置代理
设置–>高级设置–>代理:前去配置代理的ip和端口 - 代码中如何设置代理
访问百度搜索的ip服务,可以看到ip地址已经变成了代理地址,已经在代码中使用了代理1
2
3
4
5
6
7
8
9import urllib.request
url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'
handler = urllib.request.ProxyHandler(proxies={'http': '218.60.8.98:3129'})
opener = urllib.request.build_opener(handler)
r = opener.open(url)
with open('代理.html', 'wb') as fp:
fp.write(r.read())