1、本博文中代码是转载内容,原文章地址如下:
https://blog.csdn.net/submit66/article/details/78631342?utm_source=blogxgwz1
2、只是在原文代码的基础上稍作修改,添加了一些注释及无关紧要的代码
3、本篇博文涉及知识点如下:
- ①创建类、创建函数
- ②创建新线程
- ③用浏览器检查网页元素
- ④使用BeautifulSoup获取网页内容
- ⑤保存网页文本内容到本地文件
- ⑥下载图片
- ⑦时间戳
4、代码功能为获取“豆瓣电影top250”页面的摘要信息,详细代码如下(运行环境:win7 + python3)
import requests
from bs4 import BeautifulSoup
import time
import os
import threading
'''
定义一个类,属性包含图片的序号、名称以及下载的url
'''
class Picture:
def __init__(self,pic_num,pic_name,pic_url):
self.pic_name = pic_name
self.pic_url = pic_url
self.pic_num = pic_num
'''
下载图片,因为他比较耗时,所以将其放在子线程中
'''
def download_picture(pic_list):
#设置图片保存的目录,双反斜杠\\表示转义“\”,实际的作用相当于'douban\pic\'
file_dir = "douban\pic\\"
#如果目录不存在,就创建它
if not os.path.isdir(file_dir):
os.makedirs(file_dir)
#下载图片
for index in range(len(pic_list)):
try:
#获取图片列表的单个元素
pic_url = pic_list[index]
#设置图片的名字,加上路径是指在路径下创建图片
filename = file_dir +str(pic_url.pic_num) + '_' + pic_url.pic_name + ".jpg"
# "wb"表示以二进制写入文件
# 此处".content"表示以二进制形式返回数据,下载图片及音频时需用此方式
# 此处的timeout并不是指超过5秒没下载完图片就算超时,而是指5秒内服务器没有响应连接请求就超时
with open(filename,'wb') as f_open:
f_open.write(requests.get(pic_url.pic_url,timeout=5).content)
#因为要下载250个图片,如果长时间等待,看不出程序正常运行,此处每下载10个图片在终端就提示一次
if pic_url.pic_num % 10 == 0:
print("已下载%d张图片" % (pic_url.pic_num))
except:
print("下载失败!")
#pass表示不返回任何错误提示
pass
print("爬取耗时:",time.time().__float__() - cuttentTime.__float__(),'s')
# param 分页网址的后缀,用于拼接
param = ''
# i 图片下载的个数
i = 1
#时间戳,用于计算程序运行的时间
cuttentTime = time.time()
# pic_list 存储图片名字及图片的下载地址
pic_list = []
while True:
base_url = 'https://movie.douban.com/top250' + param
my_response = requests.get(base_url,timeout = 5)
#若页面返回的状态码不是200,则显示错误
my_response.raise_for_status()
response_string = my_response.text
soup = BeautifulSoup(response_string,'lxml')
#所有的电影信息都在class为grid_view的ol标签中
ol_article = soup.find('ol',class_='grid_view')
#每部电影对应一个li
li_list = ol_article.find_all('li')
#获取电影的信息
for index in range(len(li_list)):
div_item_info = li_list[index].find('div',class_='info')
div_hd = div_item_info.find('div',class_='hd')
div_bd = div_item_info.find('div',class_='bd')
title_list = div_hd.find_all('span')
#获取电影名
title_str = div_hd.find('span',class_='title').getText()
#获取电影别名
if div_hd.find('span',class_='other'):
alias_title_str = div_hd.find('span',class_='other').getText().\
replace(' ','').replace(' ','').replace('/','',1)
#获取导演、演员、上映年、电影类型等信息
content_description_str = div_bd.p.getText().replace(' ','\t\t').\
replace(' ','').replace(' / ','/')
#获取评价星级
rating_star = div_bd.div.find_all('span')[1].getText()
#获取评论数量
comment_str = div_bd.div.find_all('span')[3].getText()
#获取一句话影评
if div_bd.find('p',class_='quote'):
quote_str = div_bd.find('p',class_='quote').span.getText()
#将抓取的信息存入本地文档
file_dir = "douban\\"
if not os.path.isdir(file_dir):
os.makedirs(file_dir)
filename = file_dir + "douban250.txt"
#此处的"encoding='utf-8'",以utf-8编码创建打开文件,为防止写入文件编码错误
with open(filename,'a',encoding='utf-8') as f_open:
f_open.write("\n第%d个电影--------------------------------------" % (i))
f_open.write("\n\n\t电影名称:" + title_str)
f_open.write("\n\t电影别名:" + alias_title_str)
f_open.write("\n\t电影评星:" + rating_star)
f_open.write("\n\t评价数量:" + comment_str)
f_open.write("\n\t电影一句话总结:" + quote_str)
f_open.write("\n\t电影大致内容信息:" + content_description_str)
#获取图片信息
div_pic = li_list[index].find('div',class_='pic')
pic_list.append(Picture(i,div_pic.a.img.get('alt'),div_pic.a.img.get('src')))
i += 1
#获取分页信息
div_paginator = soup.find('div',class_='paginator')
next_url = div_paginator.find('span',class_='next')
#如果没有link则退出while循环,用来判断已经到达最后一页
if not next_url.link:
break
# 获取link的某个属性,可以使用get方法
param = next_url.link.get('href')
'''
在所有数据内容爬取完毕后开始一个新的线程下载图片,这里还非得用threading模块了,
因为它开启的派生线程在运行时候,主线程不会退出,直至派生线程执行完毕
但是如果派生线程被设置为守护线程,即设置setDaemon为true的话,
主线程退出派生线程也就不执行了(但是这个不是我们想要的)
如果直接使用thread模块就会存在主线程提前退出派生线程无法执行完毕,导致下载失败的情况
'''
#这里我不明白原作者为什么要开启一个新线程去下载图片,与直接批量下载图片有什么区别吗,
# 此问题待以后学习更多线程相关的知识后再分析
try:
threa_download = threading.Thread(target=download_picture,args=(pic_list,))
threa_download.setDaemon(False)
threa_download.start()
except:
print('Error: unable to start thread')