我会把每次在网络上做的爬虫和数据分析都发上来,至少每周一次,这既是为了督促自己,也是希望能把自己遇到的问题及解决方法和大家分享。
这次爬虫使用的是pyquery和requests,requests主要是用来获取图片的字节信息的,没在pyquery中找到,知道的朋友也可以说一下,大家一起进步~!
因为豆瓣电影中比较友好,所以我只设置了一个伪装浏览器的headers就开始爬取了,很顺畅地获取到了网页信息。
接下来是解析网页了,因为影片信息都在单独
def get_html(url): # 获取页面
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
} # 构建请求头
doc = pq(url, headers=headers) # 请求页面,获取返回url的html
return doc
def parse_html(doc): # 解析页面
li_list = doc(".article .grid_view").find("li") # 获取影片内容所在区域
for item in li_list.items(): # 对同一页中每部影片遍历,获取每部影片相关内容
try:
movie_poster_src = item(".item .pic a").find("img").attr("src") # 获取海报的资源地址
movie_poster_b = requests.get(movie_poster_src).content # 获取海报,并将图片转为字节
movie_num = item(".item .pic em").text() # 获取影片排行的文本值
movie_url = item(".item .info .hd a").attr("href") # 获取影片url地址
movie_name_list = item(".item .info .hd .title").text().split("/") # 获取影片名称列表
movie_name = "/".join(clear_strip(movie_name_list)) # 对影片名称去除左右两边空格并以/分开
movie_score = item(".item .info .bd .star .rating_num").text() # 获取影片评分
movie_intro = item(".item .info .bd .quote span").text() # 获取影片简介
movie_info = item(".item .info .bd p").text() # 获取影片中信息集合,利用正则一次获取
movie_director = "".join(clear_strip(re.findall(r"导演:(.*?)\xa0", movie_info))) # 去除左右空格并获取导演信息
movie_star = "".join(re.findall(r"主演:(.*?)\n", movie_info)) # 去除左右空格获取主演信息
movie_year = "".join(re.findall(r"(\d{4}.*?)\xa0/\xa0", movie_info)) # 获取上映年份信息
movie_country = "/".join(re.findall(r"\d{4}.*?\xa0/\xa0(.*?)\xa0/\xa0", movie_info)[0].split()) # 去除空格获取拍摄国家信息
movie_type = "/".join(re.findall(r'\d{4}.*?\xa0/\xa0.*?\xa0/\xa0(.*?)$', movie_info)[0].split()[:-1]) # 去除空格获取拍摄电影类型信息
movie_comment_count = item(".item .info .bd .star .rating_num").siblings().text().strip() # 获取评价人数
yield [movie_num, movie_name, movie_director, movie_star, movie_year, movie_country, movie_type, movie_score,
movie_comment_count, movie_intro, movie_url, movie_poster_b] # 每次循环以列表形式返回值
except Exception as e:
print("错误:", repr(e)) # 若出现错误,打印错误信息
continue
else:
print("此页数据爬取成功!") # 每爬取成功一页打印一次
忘记说了,我爬取时是通过定义不同功能的函数,最终通过判断是否在当前文件下执行,即“if __name__ = “__main__””来执行的,并没有封装到一个类里。
网页整体分布很规律,找到相应位置即可,只有在导演主演上映时间那一块是一堆,利用\xa0
加正则可以很轻松把导演那一堆信息提取出来。其中\xa0
为HTML中的
。
接下来是一个翻页操作,我直接通过多点几页分析每页网址规律写出来每页网址,然后调用获取网页和解析网页两个函数就行。
最后我把获取的数据保存为csv格式。
countries = data["movie_country"].str.split("/").apply(pd.Series) # 获取所有国家,并将一行中含有多个国家的多个国家分开
country = countries.apply(pd.value_counts).fillna(0) # 对国家进行计数并填充空值
country.columns = ["area1", "area2", "area3", "area4", "area5", "area6"] # 修改列名
country["area_count"] = country.sum(axis=1)
country.sort_values("area_count", ascending=False, inplace=True) # 以总和列降序排序
# print(country)
mp.subplot(121)
country.area_count.head(10).plot(kind="bar", color="orangered") # 绘制前10名数据条形图
mp.title("国家参与制作影片数排行TOP10")
mp.xlabel("地区名", fontsize=12) # 设置x轴标签
mp.ylabel("频次", fontsize=12)
mp.yticks(range(0, 180, 20), [0, 20, 40, 60, 80, 100, 120, 140, 160]) # 设置y轴坐标
mp.xticks(range(10), country.head(10).index, rotation=45) # 设置x轴坐标,并让x轴坐标标签旋转45度
# axes[0, 0].xaxis.set_major_locator(10)
for x, y in zip(range(10), country.area_count.head(10)):
mp.text(x, y, "%d" % y, ha="center", va="bottom") # 为每个条形图增加文本注释
代码和国家差不多,其实doubanTOP250中基本所有分析都是一样的格式,没多大差别。
还有词云图。
词云图代码如下,使用的是最基础设置,并用了jiebe分词。
text_type = data["movie_type"].str.split("/").tolist() # 将获取的电影类型数据做split分割后转为列表
text = "".join(jieba.cut(str(text_type).replace("'", ""), cut_all=False)) # 以空格连接列表并用jieba分词
# print(text)
wc_type = WordCloud(background_color="white", font_path="STXIHEI.TTF", width=900, height=600).generate(text) # 设置词云图
mp.figure("词云图分析") # 新建一图
mp.subplot(121)
mp.imshow(wc_type, interpolation="bilinear") # 绘制热图函数
mp.title("电影类型词云图分析")
mp.axis("off")
评价人数获取到后还有“人评价”,因此得先把它去掉转为数值型,才能进行分析。使用strip("人评价")
就可以很方便地去掉,在这儿我必须强调一点,熟悉python中最基础的函数,有时候的确事半功倍,像我之前一直以为strip()只能去除左右空格,处理这儿的时候可费了一些功夫。
data["movie_comment_count"] = data["movie_comment_count"].str.strip("人评价").astype(int)
# print(data)
num_comment_corr = data["movie_num"].corr(data["movie_comment_count"])
ax5 = mp.subplot(gs[3, :])
ax5.scatter(data["movie_num"], data["movie_comment_count"], s=5, label="%.2f" % num_comment_corr)
ax5.set_title("电影排名与评价人数关系")
ax5.xaxis.set_major_locator(ticker.MultipleLocator(5))
ax5.set_xlim(0, 255)
ax5.tick_params(rotation=45)
ax5.set_xlabel("电影排名", fontsize=12)
ax5.set_ylabel("评价人数", fontsize=12)
ax5.set_yticklabels(())
ax5.legend()
这里用了栅格布局器,因为我把涉及到数值的图都放在了一起。就在下面↓↓↓
,个人感觉看得很舒畅~~
重点说一下右边两个散点图, 因为想给它们俩设置同一标题,网上搜了好久也没想要的,我就按我自己的来了,效果还不错。
新设置一坐标,位置找好后,把这个坐标的背景、脊柱线颜色都设为
"none"
,记住是"none"
不是None
# h. 评分与上映年份关系
data.drop(index=[58, 168], axis=0, inplace=True)
ax2 = mp.subplot(gs[:3, 1]) # 在第一列绘制占三行高的图
ax2.scatter(data["movie_score"], data["movie_year"]) # 绘制散点图
ax2.yaxis.set_major_locator(ticker.MultipleLocator(5))
ax2.set_xlabel("评分", fontsize=12)
ax2.set_ylabel("上映年份", fontsize=12)
ax3 = mp.subplot(gs[:3, 2]) # 在第二列绘制占三行高的图
ax3.scatter(data["movie_year"], data["movie_score"])
ax3.xaxis.set_major_locator(ticker.MultipleLocator(5))
ax3.tick_params(labelsize=9, rotation=45)
ax3.set_xlabel("上映年份", fontsize=12)
ax3.set_ylabel("评分", fontsize=12)
ax4 = mp.axes([0.4, 0.1, 0.5, 0.8], zorder=0, facecolor="w", alpha=1) # 目的是为了给两个散点图设置统一标题
ax4.set_title("评分与上映年份关系")
ax4.set_xticks(())
ax4.set_yticks(())
ax4.patch.set_facecolor("none") # 设置坐标系无背景颜色
# ax4.patch.set_alpha(0)
ax = mp.gca() # 获取当前绘图区域
ax.spines["right"].set_color("none")
ax.spines["left"].set_color("none")
ax.spines["top"].set_color("none")
ax.spines["bottom"].set_color("none") # 将坐标系四条“脊柱”颜色设为无
ok,以上就是我对豆瓣影片250的分析,虽然关于豆瓣250分析可以说是烂大街了,但我觉着每个人分析的时候总会有不一样的想法。我在我的公众号里也有这篇分析,里面有一些我对这些数据的个人分析,感兴趣的朋友可以看一下!多谢观看!!