提取出猫眼电影 TOP100 榜的电影名称、时间、评分、图片等信息,提取的站点 URL 为:http://maoyan.com/board/4,提取的结果以文件形式保存下来。
添加Requsets依赖库。注意不是Request
本节我们需要抓取的目标站点为:http://maoyan.com/board/4,打开之后便可以查看到榜单的信息,如图
网页下滑到最下方可以发现有分页的列表,我们点击一下第二页观察一下页面的URL和内容发生了怎样的变化,如图
可以发现页面的 URL 变成了:http://maoyan.com/board/4?offset=10,相比之前的URL多了一个参数,那就是 offset=10,而目前显示的结果是排行 11-20 名的电影,初步推断这是一个偏移量的参数,再点击下一页,发现页面的 URL 变成了:http://maoyan.com/board/4?offset=20,参数 offset 变成了 20,而显示的结果是排行 21-30 的电影。
由此可以总结出规律,offset 代表了一个偏移量值,如果偏移量为 n,则显示的电影序号就是 n+1 到 n+10,每页显示 10 个。所以如果想获取 TOP100 电影,只需要分开请求 10 次,而 10 次的 offset 参数设置为 0,10,20,...,90 即可,这样获取不同的页面结果之后再用正则表达式提取出相关信息就可以得到 TOP100 的所有电影信息了。
接下来我们用代码实现这个过程,首先抓取第一页的内容,我们实现一个 get_one_page() 方法,传入 url 参数,然后将抓取的页面结果返回,然后再实现一个 main() 方法调用一下,初步代码实现如下:
import requests
def get_one_page(url):
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
def main():
url = 'http://maoyan.com/board/4'
html = get_one_page(url)
print(html)
if __name__ == '__main__':
main()
一般网站都会有反爬虫机制,如果发现数据无法抓取下来,那应该是检测到了你的ip爬虫,这时候可以使用代理爬虫,这一块涉及的比较多,包括代理池,到后面再慢慢了解,先给俩获取代理ip的函数,ip来自西刺
def get_ip_list(url, headers):
web_data=requests.get(url,headers=headers)
soup = BeautifulSoup(web_data.text, 'lxml')
ips = soup.find_all('tr')
ip_list = []
for i in range(1, len(ips)):
ip_info = ips[i]
tds = ip_info.find_all('td')
ip_list.append(tds[1].text + ':' + tds[2].text)
return ip_list
def get_random_ip(ip_list):
proxy_list = []
for ip in ip_list:
proxy_list.append('http://' + ip)
proxy_ip = random.choice(proxy_list)
proxies = {'http': proxy_ip}
return proxies
#调用
url = 'http://www.xicidaili.com/nn/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
}
ip_list = get_ip_list(url, headers=headers)
proxies = get_random_ip(ip_list)
print(proxies)
# 这个 proxies 就是要填入的代理IP
# requests.get(url1, proxies=proxies, headers=headers)
首先看一下页面的真实源码
查看其中的一个条目的源代码如图:
.*?board-index.*?>(.*?)
随后需要提取电影的图片,可以看到在后面有个 a 节点,其内部有两个 img 节点,经过检查后发现第二个 img 节点的 data-src属性是图片的链接,在这里提取第二个 img 节点的 data-src属性,所以正则可以改写如下:
.*?board-index.*?>(.*?).*?data-src="(.*?)"
再往后我们需要提取电影的名称,它在后面的 p 节点内,class 为 name,所以我们可以用 name 做一个标志位,然后进一步提取到其内 a 节点的正文内容,正则改写如下:
.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?) a>
随后如果需要再提取主演、发布时间、评分等内容的话都是同样的原理,最后正则表达式写为:
.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?) a>.*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*?
这样我们一个正则表达式可以匹配一个电影的结果,里面匹配了 7 个信息,接下来我们通过调用 findall() 方法提取出所有的内容,实现一个 parse_one_page() 方法如下:
def parse_one_page(html):
pattern = re.compile(
'.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?) a>.*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*? ',
re.S)
items = re.findall(pattern, html)
print(items)
def parse_one_page(html):
pattern = re.compile(
'.*?board-index.*?>(.*?).*?data-src="(.*?)".*?name.*?a.*?>(.*?) a>.*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*? ',
re.S)
items = re.findall(pattern, html)
for item in items:
yield {
'index': item[0],
'image': item[1],
'title': item[2].strip(),
'actor': item[3].strip()[3:] if len(item[3]) > 3 else '',
'time': item[4].strip()[5:] if len(item[4]) > 5 else '',
'score': item[5].strip() + item[6].strip()
}
{'image': 'http://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c', 'actor': '张国荣,张丰毅,巩俐', 'score': '9.6', 'index': '1', 'title': '霸王别姬', 'time': '1993-01-01(中国香港)'}
{'image': 'http://p0.meituan.net/movie/__40191813__4767047.jpg@160w_220h_1e_1c', 'actor': '蒂姆·罗宾斯,摩根·弗里曼,鲍勃·冈顿', 'score': '9.5', 'index': '2', 'title': '肖申克的救赎', 'time': '1994-10-14(美国)'}
{'image': 'http://p0.meituan.net/movie/fc9d78dd2ce84d20e53b6d1ae2eea4fb1515304.jpg@160w_220h_1e_1c', 'actor': '让·雷诺,加里·奥德曼,娜塔莉·波特曼', 'score': '9.5', 'index': '3', 'title': '这个杀手不太冷', 'time': '1994-09-14(法国)'}
{'image': 'http://p0.meituan.net/movie/23/6009725.jpg@160w_220h_1e_1c', 'actor': '格利高利·派克,奥黛丽·赫本,埃迪·艾伯特', 'score': '9.1', 'index': '4', 'title': '罗马假日', 'time': '1953-09-02(美国)'}
{'image': 'http://p0.meituan.net/movie/53/1541925.jpg@160w_220h_1e_1c', 'actor': '汤姆·汉克斯,罗宾·怀特,加里·西尼斯', 'score': '9.4', 'index': '5', 'title': '阿甘正传', 'time': '1994-07-06(美国)'}
{'image': 'http://p0.meituan.net/movie/11/324629.jpg@160w_220h_1e_1c', 'actor': '莱昂纳多·迪卡普里奥,凯特·温丝莱特,比利·赞恩', 'score': '9.5', 'index': '6', 'title': '泰坦尼克号', 'time': '1998-04-03'}
{'image': 'http://p0.meituan.net/movie/99/678407.jpg@160w_220h_1e_1c', 'actor': '日高法子,坂本千夏,糸井重里', 'score': '9.2', 'index': '7', 'title': '龙猫', 'time': '1988-04-16(日本)'}
{'image': 'http://p0.meituan.net/movie/92/8212889.jpg@160w_220h_1e_1c', 'actor': '马龙·白兰度,阿尔·帕西诺,詹姆斯·凯恩', 'score': '9.3', 'index': '8', 'title': '教父', 'time': '1972-03-24(美国)'}
{'image': 'http://p0.meituan.net/movie/62/109878.jpg@160w_220h_1e_1c', 'actor': '周星驰,巩俐,郑佩佩', 'score': '9.2', 'index': '9', 'title': '唐伯虎点秋香', 'time': '1993-07-01(中国香港)'}
{'image': 'http://p0.meituan.net/movie/9bf7d7b81001a9cf8adbac5a7cf7d766132425.jpg@160w_220h_1e_1c', 'actor': '柊瑠美,入野自由,夏木真理', 'score': '9.3', 'index': '10', 'title': '千与千寻', 'time': '2001-07-20(日本)'}
随后将提取的结果写入文件,在这里直接写入到一个文本文件中,通过 json 库的 dumps() 方法实现字典的序列化,并指定 ensure_ascii 参数为 False,这样可以保证输出的结果是中文形式而不是 Unicode 编码,代码实现如下:
def write_to_json(content):
with open('result.txt', 'a') as f:
print(type(json.dumps(content)))
f.write(json.dumps(content, ensure_ascii=False,).encode('utf-8'))
通过调用 write_to_json() 方法即可实现将字典写入到文本文件的过程,此处的 content 参数就是一部电影的提取结果,是一个字典。
def main():
url = 'http://maoyan.com/board/4'
html = get_one_page(url)
for item in parse_one_page(html):
write_to_json(item)
if __name__ == '__main__':
for i in range(10):
main(offset=i * 10)
将 main() 方法修改一下,接收一个 offset 值作为偏移量,然后构造 URL 进行爬取,实现如下
def main(offset):
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_one_page(url)
for item in parse_one_page(html):
print(item)
write_to_file(item)
import json
import requests
from requests.exceptions import RequestException
import re
import time
def get_one_page(url):
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
except RequestException:
return None
def parse_one_page(html):
pattern = re.compile('.*?board-index.*?>(\d+).*?data-src="(.*?)".*?name">(.*?) a>.*?star">(.*?).*?releasetime">(.*?)'
+ '.*?integer">(.*?).*?fraction">(.*?).*? ', re.S)
items = re.findall(pattern, html)
for item in items:
yield {
'index': item[0],
'image': item[1],
'title': item[2],
'actor': item[3].strip()[3:],
'time': item[4].strip()[5:],
'score': item[5] + item[6]
}
def write_to_file(content):
with open('result.txt', 'a', encoding='utf-8') as f:
f.write(json.dumps(content, ensure_ascii=False) + '\n')
def main(offset):
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_one_page(url)
for item in parse_one_page(html):
print(item)
write_to_file(item)
if __name__ == '__main__':
for i in range(10):
main(offset=i * 10)
time.sleep(1)
最后,跑起来。
就能得到一个txt文件,如下:
最后,祝各位程序猿长命百岁
万事开头难,然后中间难,最后结尾难