今天练习网络爬虫,爬取豆瓣电影Top 250,其中每部电影有排名、评分、电影名、导演等信息,分布在不同的标签下面,而每部电影的所有信息都在一个大的
标签下,因此需要获取子元素的内容。查了一下只看到有人用循环嵌套的方法遍历子元素,感觉略麻烦,后来发现.contents属性可以方便地获取子元素内容,因此采用。
首先爬取第一页的内容,将所有电影的信息存入targets,用for循环遍历(使用break的目的是只研究第一部电影的信息,以节省时间):
首先爬取第一页的内容,将所有电影的信息存入targets,用for循环遍历(使用break的目的是只研究第一部电影的信息,以节省时间):
import requests
import re
import bs4
def parse_one_page(url):
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0'}
res = requests.get(url, headers=headers)
soup = bs4.BeautifulSoup(res.text, 'html.parser')
targets = soup.find_all('div', class_='item')
for each in targets:
print(each)
break
def main():
global top250
top250 = []
url = 'https://movie.douban.com/top250?start={}&filter='.format(25 * 0)
parse_one_page(url)
if __name__ == '__main__':
main()
运行结果如下:
<div class="item">
<div class="pic">
<em class="">1</em>
<a href="https://movie.douban.com/subject/1292052/">
<img alt="肖申克的救赎" class="" src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg" width="100"/>
</a>
</div>
<div class="info">
<div class="hd">
<a class="" href="https://movie.douban.com/subject/1292052/">
<span class="title">肖申克的救赎</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飞(港) / 刺激1995(台)</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p class="">
导演: 弗兰克·德拉邦特 Frank Darabont 主演: 蒂姆·罗宾斯 Tim
Robbins /...<br/>
1994 / 美国 / 犯罪 剧情
</p>
<div class="star">
<span class="rating5-t"></span>
<span class="rating_num" property="v:average">9.6</span>
<span content="10.0" property="v:best"></span>
<span>1338214人评价</span>
</div>
<p class="quote">
<span class="inq">希望让人自由。</span>
</p>
</div>
</div>
</div>
可以对比这一部分的源代码:
<div class="item">
<div class="pic">
<em class="">1</em>
<a href="https://movie.douban.com/subject/1292052/">
<img width="100" alt="肖申克的救赎" src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp" class="">
</a>
</div>
<div class="info">
<div class="hd">
<a href="https://movie.douban.com/subject/1292052/" class="">
<span class="title">肖申克的救赎</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飞(港) / 刺激1995(台)</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p class="">
导演: 弗兰克·德拉邦特 Frank Darabont 主演: 蒂姆·罗宾斯 Tim Robbins /...<br>
1994 / 美国 / 犯罪 剧情
</p>
<div class="star">
<span class="rating5-t"></span>
<span class="rating_num" property="v:average">9.6</span>
<span property="v:best" content="10.0"></span>
<span>1338214人评价</span>
</div>
<p class="quote">
<span class="inq">希望让人自由。</span>
</p>
</div>
</div>
</div>
可以看出,第一部电影的信息被打印了出来,要想获取具体某一项信息,尝试调用.contents属性print(each.contents)
:
['\n', <div class="pic">
<em class="">1</em>
<a href="https://movie.douban.com/subject/1292052/">
<img alt="肖申克的救赎" class="" src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg" width="100"/>
</a>
</div>, '\n', <div class="info">
<div class="hd">
<a class="" href="https://movie.douban.com/subject/1292052/">
<span class="title">肖申克的救赎</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飞(港) / 刺激1995(台)</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p class="">
导演: 弗兰克·德拉邦特 Frank Darabont 主演: 蒂姆·罗宾斯 Tim
Robbins /...<br/>
1994 / 美国 / 犯罪 剧情
</p>
<div class="star">
<span class="rating5-t"></span>
<span class="rating_num" property="v:average">9.6</span>
<span content="10.0" property="v:best"></span>
<span>1338863人评价</span>
</div>
<p class="quote">
<span class="inq">希望让人自由。</span>
</p>
</div>
</div>, '\n']
发现得到一个list,每一个子元素成为list的一个元素,两两之间有换行符,list收尾也是换行符。由此推断<div class="pic">
子元素为each.contents[1]
,由此类推,电影排名为each.contents[1].contents[1].text
,电影名为each.contents[3].contents[1].contents[1].contents[1].text
,电影评分为each.contents[3].contents[3].contents[3].contents[3].text
,等等。最后,将每一部电影的信息append到列表中,并写入Excel。完整代码如下:
import requests
import re
import bs4
import openpyxl
def parse_one_page(url):
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0'}
res = requests.get(url, headers=headers)
soup = bs4.BeautifulSoup(res.text, 'html.parser')
targets = soup.find_all('div', class_='item')
for each in targets:
rank = each.contents[1].contents[1].text
name = each.contents[3].contents[1].contents[1].contents[1].text
rating = each.contents[3].contents[3].contents[3].contents[3].text
try:
quote = each.contents[3].contents[3].contents[5].contents[1].text
except:
quote = ''
s = each.contents[3].contents[3].contents[1].text
pattern = '导演: (.*?)\s主演: (.*?)...\s+(\d+)\s/\s(.*?)\s/\s(.*)\s+'
try:
director = re.search(pattern, s).group(1)
except:
director = ''
try:
hero = re.search(pattern, s).group(2)
except:
hero = ''
try:
year = re.search(pattern, s).group(3)
except:
year = ''
try:
region = re.search(pattern, s).group(4)
except:
region = ''
try:
genre = re.search(pattern, s).group(5)
except:
genre = ''
top250.append([rank, name, rating, year, region, genre, director, hero, quote])
#break
def main():
global top250
top250 = []
for i in range(10):
url = 'https://movie.douban.com/top250?start={}&filter='.format(25 * i)
parse_one_page(url)
wb = openpyxl.Workbook()
ws = wb.active
ws.append(['rank', 'name', 'rating', 'year', 'region', 'genre', 'director', 'hero', 'quote'])
for each in top250:
ws.append(each)
wb.save('douban.xlsx')
if __name__ == '__main__':
main()
以上代码鲁棒性还不够好,原因是Top 250的页面显示的信息有限,有时候导演名字过长,“演员”二字显示不完整,导致正则表达式匹配不上,于是演员等信息就不显示了。更好的方案是获取每一部电影的URL,然后逐一解析。