Python爬虫学习案例之抓取猫眼电影排行Top100
目前在自学python爬虫,接下来运用学习了的requests库和正则表达式实操一下Python爬虫初学者经常训练的一个小实战案例——爬取猫眼电影排行Top100
抓取分析
首先我们打开抓取的目标站点https://maoyan.com/board/4
同时此时页面的URL为https://maoyan.com/board/4?offset=0
我们将网页滚到最下方,发现有分页的列表,直接点击第2页,观察页面的URL和内容发生了变化,URL变为了https://maoyan.com/board/4?offset=10
,目前显示的排名为11~20的电影。
我们点击第3页,URL变为了https://maoyan.com/board/4?offset=20
,显示的排名为21~30的电影。
因此我们可以发现规律,offset代表偏移量,如果偏移量为n,则显示的电影序号为n+1到n+10,每页显示10个。所有想要获取Top100的电影,需要循环10次。
接下来就i是分析首页的源代码了,在开发者工具中的Network监听组件中查看源代码。如图所示
也可以通过request.get()方法获取首页的源代码
import requests
import re
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0',
}
response = requests.get(url,headers=headers)
if response.status_code == 200:
return response.text
else:
return None
def main(offset):
base_url = 'https://maoyan.com/board/4?offset='
url = base_url+str(offset)
html = get_one_page(url)
print(html)
if __name__=='__main__':
main(0)
输出如下:(取了其中一部分)
<dd>
<i class="board-index board-index-1">1</i>
<a href="/films/1375" title="活着" class="image-link" data-act="boarditem-click" data-val="{movieId:1375}">
<img src="//s3plus.meituan.net/v1/mss_e2821d7f0cfe4ac1bf9202ecf9590e67/cdn-prod/file:5788b470/image/loading_2.e3d934bf.png" alt="" class="poster-default" />
<img data-src="https://p0.meituan.net/movie/4c41068ef7608c1d4fbfbe6016e589f7204391.jpg@160w_220h_1e_1c" alt="活着" class="board-img" />
</a>
<div class="board-item-main">
<div class="board-item-content">
<div class="movie-item-info">
<p class="name"><a href="/films/1375" title="活着" data-act="boarditem-click" data-val="{movieId:1375}">活着</a></p>
<p class="star">
主演:葛优,巩俐,牛犇(bēn)
</p>
<p class="releasetime">上映时间:1994-05-17(法国)</p> </div>
<div class="movie-item-number score-num">
<p class="score"><i class="integer">9.</i><i class="fraction">0</i></p>
</div>
</div>
</div>
</dd>
可以看到,一部电影的信息对应的源代码就是一个dd节点,我们的目标是提取电影的排名、名称、时间、评分、图片等信息。接下来就是设计正则表达式了
正则提取
排名
根据上文源代码,我们可以发现排名信息是在class为board-index的i节点内<i class="board-index board-index-1">1</i>
,那么我们就设计正则表达式为<dd>.*?board-index.*?>(\d+)</i>
图片
同样的,上述的a节点中有两个img节点对应的是图片的信息,第二个img节点的data-src属性是图片的链接<img data-src="https://p0.meituan.net/movie/4c41068ef7608c1d4fbfbe6016e589f7204391.jpg@160w_220h_1e_1c" alt="活着" class="board-img" />
,因此更新正则表达式为<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)"
名称
电影的名称在后面的p节点内,class为name,即<p class="name"><a href="/films/1375" title="活着" data-act="boarditem-click" data-val="{movieId:1375}">活着</a></p>
。所以可以用name做一个标志位,然后进一步提取到其内a节点的正文内容,此时正则表达式改写如下:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>
主演
主演提取思路同上,根据
<p class="star">
主演:葛优,巩俐,牛犇(bēn)
</p>
则修改正则表达式为:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>
时间
时间提取思路同上,根据<p class="releasetime">上映时间:1994-05-17(法国)</p>
修改正则表达式为:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>
评分
评分提取思路同上,最终正则表达式为:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>
接下来就是编写提取页面信息的时候啦!
总代码如下:
import requests
import json
import time
import re
import random
def get_one_page(url):
headers = [{
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1',
},{
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
},{
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
}]
count = random.randint(0,2)
response = requests.get(url,headers=headers[count])
if response.status_code == 200:
return response.text
else:
return None
def parse_one_page(html):
pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>',re.S)
items = re.findall(pattern,html)
for item in items:
yield{
'index':item[0],
'image_url':item[1],
'title':item[2].strip(),
'actor':item[3].strip()[3:],
'time':item[4].strip()[5:],
'score':item[5].strip()+item[6].strip()
}
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):
base_url = 'https://maoyan.com/board/4?offset='
url = base_url+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(3)
下面针对以上程序进行简单讲解,中心思想就是循环调用main()函数,通过更改offset偏移量来对10个页面进行遍历访问,访问一个页面后如果响应状态码为200则返回响应信息,并通过parse_one_page(html)对返回的信息进行正则提取以及结果处理,同时将获取的结果写入文件。
其中,parse_one_page(html)中的下面的代码可能有些不好理解
for item in items:
yield{
'index':item[0],
'image_url':item[1],
'title':item[2].strip(),
'actor':item[3].strip()[3:],
'time':item[4].strip()[5:],
'score':item[5].strip()+item[6].strip()
}
因为函数中带有yield,我们可以把它(该函数)看作一个生成器,并且当执行到yield的时候,函数将会将item的数据处理为字典类型打包返回,返回给了main(offset)的print(item),然后将该部分item写入文件,当执行下一次 for item in parse_one_page(html)时,程序跳入parse_one_page(html)的上次离开的地方,即执行下一次parse_one_page(html)的for item in items循环,而并不是从函数开头执行。
如下图:
对于**write_to_file(content)**函数,这里就是通过JSON库的dumps()方法实现字典的序列化,并指定ensure_ascii参数为Fasle,这样就可以保证输出结果是中文形式而不是Unicode编码。
最后,成功爬取到了Top100,是真真切切感受到了爬虫的强大,善加利用真的能简化很多事情呢!
保存的result.txt文件及输出打印信息
本文地址:https://blog.csdn.net/qq_42642142/article/details/107655245