本次抓取猫眼电影Top100榜所用到的知识点:
1. python requests库
2. 正则表达式
3. csv模块
4. 多进程
正文
目标站点分析
通过对目标站点的分析, 来确定网页结构, 进一步确定具体的抓取方式.
1. 浏览器打开猫眼电影首页, 点击"榜单", 点击"Top100榜", 即可看到目标页面.
2. 浏览网页, 滚动到下方发现有分页, 切换到第2页, 发现: URL从 http://maoyan.com/board/4变换到http://maoyan.com/board/4?offset=10, 多次切换页码offset都有改变, 可以确定的是通过改变URL的offset参数来生成分页列表.
项目流程框架:
获取单页源码
1 #抓取猫眼电影TOP100榜
2 import requests
3 import time
4 from requests.exceptions import RequestException
5 def get_one_page():
6 '''获取单页源码'''
7 try:
8 url = "http://maoyan.com/board/4?offset={0}".format(0)
9 headers = {
10 "User-Agent":"Mozilla/5.0(WindowsNT6.3;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/68.0.3440.106Safari/537.36"
11 }
12 res = requests.get(url, headers=headers)
13 # 判断响应是否成功,若成功打印响应内容,否则返回None
14 if res.status_code == 200:
15 print(res.text)
16 return None
17 except RequestException:
18 return None
19 def main():
20 get_one_page()
21 if __name__ == '__main__':
22 main()
23 time.sleep(1)
执行即可得到网页源码, 那么下一步就是解析源码了
解析单页源码
导入正则表达式re模块, 对代码进行解析, 得到想要的信息.
1 import re
2
3 def parse_one_page(html):
4 '''解析单页源码'''
5 pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?name"><a.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime'
6 + '.*?>(.*?)</p>.*?score.*?integer">(.*?)</i>.*?>(.*?)</i>.*?</dd>',re.S)
7 items = re.findall(pattern,html)
8 print(items)
9 #采用遍历的方式提取信息
10 for item in items:
11 yield {
12 'rank' :item[0],
13 'title':item[1],
14 'actor':item[2].strip()[3:] if len(item[2])>3 else '', #判断是否大于3个字符
15 'time' :item[3].strip()[5:] if len(item[3])>5 else '',
16 'score':item[4] + item[5]
17 }
18 def main():
19 html = get_one_page()
20 for item in parse_one_page(html):
21 print(item)
22
23 if __name__ == '__main__':
24 main()
25 time.sleep(1)
提取出信息之后, 那么下一步就是保存到文件
保存到文件中
这里采用两种方式, 一种是保存到text文件, 另一种是保存到csv文件中, 根据需要选择其一即可.
1. 保存到text文件
1 import json
2
3 def write_to_textfile(content):
4 '''写入到text文件中'''
5 with open("MovieResult.text",'a',encoding='utf-8') as f:
6 #利用json.dumps()方法将字典序列化,并将ensure_ascii参数设置为False,保证结果是中文而不是Unicode码.
7 f.write(json.dumps(content,ensure_ascii=False) + "\n")
8 f.close()
9 def main():
10 html = get_one_page()
11 for item in parse_one_page(html):
12 write_to_textfile(item)
13
14 if __name__ == '__main__':
15 main()
16 time.sleep(1)
2. 保存到CSV文件
其文件以纯文本的形式存储表格数据
1 import csv
2 def write_to_csvfile(content):
3 '''写入到csv文件中'''
4 with open("MovieResult.csv",'a',encoding='gb18030',newline='') as f:
5 # 将字段名传入列表
6 fieldnames = ["rank", "title", "actor", "time", "score"]
7 #将字段名传给Dictwriter来初始化一个字典写入对象
8 writer = csv.DictWriter(f,fieldnames=fieldnames)
9 #调用writeheader方法写入字段名
10 writer.writeheader()
11 writer.writerows(content)
12 f.close()
13 def main():
14 html = get_one_page()
15 rows = []
16 for item in parse_one_page(html):
17 #write_to_textfile(item)
18 rows.append(item)
19 write_to_csvfile(rows)
20 if __name__ == '__main__':
21 main()
22 time.sleep(1)
单页的信息已经提取出, 接着就是提取多个页面的信息
获取多个页面
1. 普通方法抓取
1 def main(offset):
2 url = "http://maoyan.com/board/4?offset={0}".format(offset)
3 html = get_one_page(url)
4 rows = []
5 for item in parse_one_page(html):
6 #write_to_textfile(item)
7 rows.append(item)
8 write_to_csvfile(rows)
9 if __name__ == '__main__':
10 #通过遍历写入TOP100信息
11 for i in range(10):
12 main(offset=i * 10)
13 time.sleep(1)
2. 多进程抓取
1 from multiprocessing import Pool
2
3 if __name__ == '__main__':
4 # 将字段名传入列表
5 fieldnames = ["rank", "title", "actor", "time", "score"]
6 write_to_csvField(fieldnames)
7 pool = Pool()
8 #map方法会把每个元素当做函数的参数,创建一个个进程,在进程池中运行.
9 pool.map(main,[i*10 for i in range(10)])
完整代码
1 #抓取猫眼电影TOP100榜
2 from multiprocessing import Pool
3 from requests.exceptions import RequestException
4 import requests
5 import json
6 import time
7 import csv
8 import re
9 def get_one_page(url):
10 '''获取单页源码'''
11 try:
12 headers = {
13 "User-Agent":"Mozilla/5.0(WindowsNT6.3;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/68.0.3440.106Safari/537.36"
14 }
15 res = requests.get(url, headers=headers)
16 # 判断响应是否成功,若成功打印响应内容,否则返回None
17 if res.status_code == 200:
18 return res.text
19 return None
20 except RequestException:
21 return None
22 def parse_one_page(html):
23 '''解析单页源码'''
24 pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?name"><a.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime'
25 + '.*?>(.*?)</p>.*?score.*?integer">(.*?)</i>.*?>(.*?)</i>.*?</dd>',re.S)
26 items = re.findall(pattern,html)
27 #采用遍历的方式提取信息
28 for item in items:
29 yield {
30 'rank' :item[0],
31 'title':item[1],
32 'actor':item[2].strip()[3:] if len(item[2])>3 else '', #判断是否大于3个字符
33 'time' :item[3].strip()[5:] if len(item[3])>5 else '',
34 'score':item[4] + item[5]
35 }
36
37 def write_to_textfile(content):
38 '''写入text文件'''
39 with open("MovieResult.text",'a',encoding='utf-8') as f:
40 #利用json.dumps()方法将字典序列化,并将ensure_ascii参数设置为False,保证结果是中文而不是Unicode码.
41 f.write(json.dumps(content,ensure_ascii=False) + "\n")
42 f.close()
43
44 def write_to_csvField(fieldnames):
45 '''写入csv文件字段'''
46 with open("MovieResult.csv", 'a', encoding='gb18030', newline='') as f:
47 #将字段名传给Dictwriter来初始化一个字典写入对象
48 writer = csv.DictWriter(f,fieldnames=fieldnames)
49 #调用writeheader方法写入字段名
50 writer.writeheader()
51 def write_to_csvRows(content,fieldnames):
52 '''写入csv文件内容'''
53 with open("MovieResult.csv",'a',encoding='gb18030',newline='') as f:
54 #将字段名传给Dictwriter来初始化一个字典写入对象
55 writer = csv.DictWriter(f,fieldnames=fieldnames)
56 #调用writeheader方法写入字段名
57 #writer.writeheader() ###这里写入字段的话会造成在抓取多个时重复.
58 writer.writerows(content)
59 f.close()
60
61 def main(offset):
62 fieldnames = ["rank", "title", "actor", "time", "score"]
63 url = "http://maoyan.com/board/4?offset={0}".format(offset)
64 html = get_one_page(url)
65 rows = []
66 for item in parse_one_page(html):
67 #write_to_textfile(item)
68 rows.append(item)
69 write_to_csvRows(rows,fieldnames)
70
71 if __name__ == '__main__':
72 # 将字段名传入列表
73 fieldnames = ["rank", "title", "actor", "time", "score"]
74 write_to_csvField(fieldnames)
75 # #通过遍历写入TOP100信息
76 # for i in range(10):
77 # main(offset=i * 10,fieldnames=fieldnames)
78 # time.sleep(1)
79 pool = Pool()
80 #map方法会把每个元素当做函数的参数,创建一个个进程,在进程池中运行.
81 pool.map(main,[i*10 for i in range(10)])
效果展示:
最终采用写入csv文件的方式.