spider基础之requests

urllib库

request处理客户端的请求

response处理服务器的响应

parse去解析URL

robots.txt文件用于识别网站, 告诉访问者哪些数据可以爬取哪些不可以, 但是没有实际的规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from urllib import request, parse
url = "http://2018.sina.com.cn/"
# 设置请求头
headers = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
"Host": "2018.sina.com.cn",
}
dict = {
"name": "Question"
}
# 将数据转换成二进制数据
data = bytes(parse.urlencode(dict), encoding="utf8")
req = request.Request(url=url, data=data, headers=headers, method="GET")
# 客户端发出请求,设置超时时间
response = request.urlopen(req, timeout=1)
print(response.read().decode("utf-8"))

在网页中爬取数据并整理成json格式, 保存到本地的json文件中; 将图片保存到本地

安装requests包

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from json import dumps

from requests import get

import re


def get_image(url):
headers = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
}
response = get(url, headers=headers)
# 对于二进制文件使用content, 对于文本文件使用text
if response.status_code == 200:
return response.content
return None


def get_page(url):
headers = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
}
response = get(url, headers=headers)
if response.status_code == 200:
return response.text
return None


def get_all_page():
all_page_list = []
for i in range(10):
offset = i * 10
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_page(url)
result_list = parse_one_page(html)
all_page_list += result_list
return all_page_list


def parse_one_page(html):
# 获取电影名
pattern = re.compile(r'movieId.*?>.*?<img src.*?>.*?<img.*?alt="(.*?)" class.*?', re.S)
movie_names = re.findall(pattern, html)
# 获取主演
pattern = re.compile(r'<p class="star">(.*?)</p>', re.S)
movie_actors = re.findall(pattern, html)
# 上映时间
pattern = re.compile(r'<p class="releasetime">(.*?)</p>', re.S)
movie_releases = re.findall(pattern, html)
# 排名
pattern = re.compile(r'<i class.*?board-index-.*?">(.*?)</i>', re.S)
movie_indexes = re.findall(pattern, html)
# 分数
pattern = re.compile(r'integer">(.*?)</i>.*?fraction">(.*?)</i>', re.S)
movie_scores = re.findall(pattern, html)
# 电影图片
pattern = re.compile(r'movieId.*?<img.*?<img data-src="(.*?)"', re.S)
movie_pics = re.findall(pattern, html)
result_list = []
for i in range(len(movie_actors)):
result_dict = {}
result_dict['name'] = movie_names[i]
result_dict['actor'] = movie_actors[i].strip()
result_dict['release'] = movie_releases[i]
result_dict['index'] = movie_indexes[i]
result_dict['score'] = movie_scores[i][0]+movie_scores[i][1]
result_dict['pic'] = movie_pics[i]
result_list.append(result_dict)
return result_list


def write_image_files(result_list):
# 将图片保存到本地
for item in result_list:
pic_name = item['pic'].split('/')[-1].split('@')[0]
# print(pic_name)
content = get_image(item['pic'].split('@')[0])
with open('./images/%s' % pic_name, 'wb') as f:
f.write(content)


def write_json(result_list):
# 将文本文件转换为json格式, 并且格式不使用ascii码格式
json_list = dumps(result_list, ensure_ascii=False)
with open('./files/maoyan_top100.json', 'w', encoding='utf-8') as f:
f.write(json_list)


def main():
# html = get_page('http://maoyan.com/board/4')
# result = parse_one_page(html)
# print(result)
result = get_all_page()
# 将图片保存到本地
write_image_files(result)
# 将内容保存到本地的json文件
write_json(result)


if __name__ == '__main__':
main()

返回响应, 对于二进制文件使用reponse.content, 对于文本文件使用reponse.text

lxml

使用pip install lxml进行安装包, 然后就能使用关键的方法etree

  • 匹配所有节点

    1
    2
    3
    # 注: html是在requests相关方法返回的text或content
    etree_html = etree.HTML(html)
    result = etree_html.xpath('//*')
  • 匹配所有子节点

    1
    result = etree_html.xpath('//span/text()')
  • 查找元素子节点

    1
    result = etree_html.xpath('//div/p/text()')
  • 查找元素所有子孙节点

    1
    result = etree_html.xpath('//div[@class="channel-item"] | //span[@class="pubtime"]/../span/a/text()')
  • 父节点

    1
    result = etree_html.xpath('//span[@class="pubtime"]/../span/a/text()')
  • 属性匹配

    1
    [@class="xxx"]
  • 文本匹配

    1
    2
    3
    4
    # 获取对应标签下的文本 /text()

    # 获取所有文本 //text()
    result = etree_html.xpath('//div[@class="article"]//text()')
  • 属性获取

    1
    2
    result = etree_html.xpath('//div[@class="article"]/div/div/@class')[0]
    result = etree_html.xpath('//div[@class="bd"]/h3/a/@href')
  • 属性多值匹配

    1
    2
    3
    # or, and, mod, //book | //cd, + - * div = != < > <= >=
    # div就是除
    result = etree_html.xpath('//span[@class="pubtime" and contains(text(), "11:")]/text()')
  • 按序选择

    1
    2
    # 拿取第一个对象 拿最后一个 位置在1,2的 倒数第三个
    [1] [last()] [poistion() < 3] [last() -2]
  • 节点轴

    1
    2
    3
    4
    5
    6
    7
    //li/ancestor::*  所有祖先节点
    //li/ancestor::div div这个祖先节点
    //li/attribute::* attribute轴,获取li节点所有属性值
    //li/child::a[@href="link1.html"] child轴,获取直接子节点
    //li/descendant::span 获取所有span类型的子孙节点
    //li/following::* 选取文档中当前节点的结束标记之后的所有节点
    //li/following-sibling::* 选取当前节点之后的所用同级节点

beautifulsoup

pip install beautifulsoup4

虽然使用时from bs4 import BeautifulSoup 但安装的是beautifulsoup4

解析器

1
2
3
4
Python 标准库 BeautifulSoup(html, “html.parser”) 速度一般,容错能力好
lxml HTML解析器 BeautifulSoup(html, “lxml”) 速度快,容错好
lxml xml解析器 BeautifulSoup(markup, “xml”) 速度快,唯一支持xml
html5lib BeautifulSoup(markup, “html5lib”) 容错性高,速度慢
  • 获取beautifulsoup对象

    1
    2
    # 第一个参数是返回的网页内容 , 第二个参数是解析器
    soup = BeautifulSoup(html, 'lxml')
  • 对网页进行缩进保持显示

    1
    soup.prettify()
  • head标签里面的title的文字内容

    1
    soup.title.string
  • 获取第一个p标签和p标签的名字

    1
    soup.p    soup.p.name
  • 获取第一张图片的src属性

    1
    2
    3
    4
    # 方法1
    soup.img.attrs["src"]
    # 方法2
    soup.img['src']

嵌套选择

1
2
3
<head>
<title>this is title</title>
</head>

如上拿取title标签中的内容, 使用soup.head.title.string , soup的节点都为bs4.element.Tag类型

关联选择

对部分元素没有特征定位, 使用找到最近的能定位的元素, 再进行相对定位

1
2
3
4
5
6
7
8
9
print(soup.p.contents) # 取p节点下⾯面所有⼦子节点列列表
print(soup.p.descendants) #取p节点所有⼦子孙节点
print(soup.a.parent) # 取⽗父节点
print(soup.a.parents) # 取所有祖先节点
print(soup.a.next_sibling) # 同级下⼀一节点
print(soup.a.previous_sibling) # 同级上⼀一节点
print(soup.a.next_siblings) # 同级所有后⾯面节点
print(soup.a.previous_siblings) # 同级所有前⾯面节点
print(list(soup.a.parents)[0].attrs['class'])

css选择器

beautifulsoup提供的最好使用查找元素或属性的方法, 通过一层一层的查找,

​:six_pointed_star: 如果是嵌套的选择器, 两种标签间需要使用空格来隔开, 类选择前加上. ,id选择器前加上#

soup.select(‘#top-local .top-sidebar ul li a’)

​:six_pointed_star: 如果对一个标签中有两个类选择器, 如class=”middle-local middle-local-color”, 使用时他们之间不能使用空格,

soup.select(‘.middle-local.middle-local-color’)

json与python格式转换

1
2
3
4
5
6
写入json数据到文件 json.dump(data, f)  f是文件对象
读取json文件 json.load(f)


json数据转python数据 json.loads(json_data)
python数据转json数据 json.dumps(str_data, ensure_ascii=False)

Ajax类型数据抓取

对于直接在网页中显示的文本,图片或其他信息可以直接使用上述的方式进行抓取, 但是对于通过ajax即时刷新的数据, 我要通过查找数据接口, 通过测试是否能得到数据, 反复调试的方式去解决

​:bust_in_silhouette:​ 通过测试拿到的Api, 并将数据进行分析, 比如分页其是否有page字段, 然后进行改变page值或取出变量的值, 观察其是否能拿到数据. 对拿到的数据进行分析, 再去取需要的字段。

script加载的数据

对于script加载出的数据, 因为requests返回的网页是在script加载前获取的网页, 因此我们要拿到这些数据需要通过查看 network –> response, 再去看script中的数据, 去拿到关键的字段

​:zoom: 例如

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
import re

from requests import get


def get_page(url):
headers = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
}
response = get(url, headers=headers)
if response.status_code == 200:
return response.text
return None


def parse_one_page(html):
# 获取路径, 通过正则表达式去拿到关键的部分
pattern = re.compile(r'downurls=.*?集\$(.*?)";', re.S)
movie_url = re.findall(pattern, html)
return movie_url

def main():
url = 'https://www.mkv99.com/vod-detail-id-9462.html'
html = get_page(url)
urls = parse_one_page(html)
print(urls)

if __name__ == '__main__':
main()

# ['ed2k://|file|夜魔侠.Marvels.Daredevil.S01E01.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|527863576|2d1c82c2c008e2ee1530e8414907a914|h=yrfispeakmuri5f22rxgzpkfcbk24qvm|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E02.中英字幕.WEB-HR.AAC.1024X576.x264.V2.mp4|524859002|a83cc090336953ee56f879f619bc56c9|h=53dvm77bdjbc7kllnfjt7yrdzeajr66i|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E03.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|518647797|9c9709e370cd0c5a38ec4f7e9ce0384f|h=5pxbvbdvrem2lpyjbl2njsvotepadyul|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E04.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|521235766|47bc6aa802b7a5512e2638dd4af40e78|h=e5ixi47l7lfv774bbuy532vagw2eqclt|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E05.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|555440046|f7652568822929a14be2d453c1459c6a|h=4uu22psmdjrchkqooqr5w6ieccxjw5u5|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E06.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|480630134|53dc20e942f84ee94fd48349b01af6e0|h=ki4lptq3wd37nzt2vn4ap3snu76wc4xo|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E07.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|500199767|77eb276a1b00bf691ee4c8b563a4ac80|h=amssg44wp464tzrxmhc3ux5hkpg2nq57|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E08.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|518658368|dbe5bdd03ecd9b57aa859c170fa1abc0|h=qbembxlyrohqh7gfbcwint2smm7b3lxb|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E09.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|571996837|1dade15505d478124f1f5a5ee17fb5c1|h=zsqhucbfikp4kaf2c3xls3eokcv45rms|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E10.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|561720273|1cebc714cf8c2b4831f5575801e14685|h=y5vkgxwv74eb6ugagurplsmf2ub24tr7|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E11.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|583884398|ea8ec771a1137ca9b555452d00318722|h=xkqfzn456ic7m4glpx2frebdcddk4x7g|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E12.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|593112458|a908a3ca23161d816c31a3821ad2def1|h=bt2kqwyolsx3fscyzkqmd7vuw2djnnli|/#', 'ed2k://|file|夜魔侠.Marvels.Daredevil.S01E13.End.中英字幕.WEB-HR.AAC.1024X576.x264.mp4|554521285|9ea56d4b04de5ad4bf6b7ade7394dc24|h=4wv4kcu6hibta7pgzw7zgyckrpp2h4yf|/#']
# 如上述的内容,放在迅雷或电驴中进行解析就可以下载
-------------end-------------
0%