简介: 又回到豆瓣了,这是多适合爬虫的网站 :)
1、这次的对象是top 250 , 网址: https://movie.douban.com/top250 目的是对这250部电影的分类做一个统计。
2、实际有其他人做了分析,正好看到,发现其代码比较漂亮,所以研读,顺便复习检阅自己学习成果。秉承一贯的作风,做一定的细节分析。先看下原文代码:
# -*- coding: utf-8 -*-
# !/usr/bin/env python
from lxml import etree
import requests
import pymysql
import matplotlib.pyplot as plt
from pylab import *
import numpy as np
# 连接mysql数据库
conn = pymysql.connect(host = 'localhost', user = 'root', passwd = '54545454', db = 'douban', charset = 'utf8')
cur = conn.cursor()
cur.execute('use douban')
def get_page(i):
url = 'https://movie.douban.com/top250?start={}&filter='.format(i)
html = requests.get(url).content.decode('utf-8')
selector = etree.HTML(html)
# //*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]
content = selector.xpath('//div[@class="info"]/div[@class="bd"]/p/text()')
print(content)
for i in content[1::2]:
print(str(i).strip().replace('\n\r', ''))
# print(str(i).split('/'))
i = str(i).split('/')
i = i[len(i) - 1]
# print('zhe' +i)
# print(i.strip())
# print(i.strip().split(' '))
key = i.strip().replace('\n', '').split(' ')
print(key)
for i in key:
if i not in douban.keys():
douban[i] = 1
else:
douban[i] += 1
def save_mysql():
print(douban)
for key in douban:
print(key)
print(douban[key])
if key != '':
try:
sql = 'insert douban(类别, 数量) value(' + "\'" + key + "\'," + "\'" + str(douban[key]) + "\'" + ');'
cur.execute(sql)
conn.commit()
except:
print('插入失败')
conn.rollback()
def pylot_show():
sql = 'select * from douban;'
cur.execute(sql)
rows = cur.fetchall()
count = []
category = []
for row in rows:
count.append(int(row[2]))
category.append(row[1])
print(count)
y_pos = np.arange(len(category))
print(y_pos)
print(category)
colors = np.random.rand(len(count))
plt.barh()
plt.barh(y_pos, count, align='center', alpha=0.4)
plt.yticks(y_pos, category)
for count, y_pos in zip(count, y_pos):
plt.text(count, y_pos, count, horizontalalignment='center', verticalalignment='center', weight='bold')
plt.ylim(+28.0, -1.0)
plt.title(u'豆瓣电影250')
plt.ylabel(u'电影分类')
plt.subplots_adjust(bottom = 0.15)
plt.xlabel(u'分类出现次数')
plt.savefig('douban.png')
if __name__ == '__main__':
douban = {}
for i in range(0, 250, 25):
get_page(i)
save_mysql()
pylot_show()
cur.close()
conn.close()
3、首先是他用了selector = etree.HTML(html) 而我用的比较多的是bs4,应该是殊途同归。他的xpath路径是:
content = selector.xpath('//div[@class="info"]/div[@class="bd"]/p/text()')
而我用的偷懒模式:直接浏览器找到的:
selector.xpath('//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]/text()')
注意,默认到P[1]的路径,因为是需要读取里面的文本部分,所以加入/text()
4、这样就得到了第一页的影视信息的文本,不过如果用ipthon分步操作查看, 那简直是逆天的格式,如图:
5、必须进行文字提取处理了。
首先,这一大坨的,为了简单,我们用单元测试的思想,先调试第一页第一个,这里之前已经设置从:
url = 'https://movie.douban.com/top250?start={}&filter='.format(i)
修改为:
url = 'https://movie.douban.com/top250?start={1}&filter='
同时,观察整个循环输出的格式是一个列表,每个元素是一个字符串,那么第一个电影的电影信息对应的字符串是:
'\n 导演: 弗兰克·德拉邦特 Frank Darabont\xa0\xa0\xa0主演: 蒂姆·罗宾斯 Tim Robbins /...', '\n 1994\xa0/\xa0美国\xa0/\xa0犯罪 剧情\n '
和原始网页的信息对照,实际就是第一部分包含导演主演信息,第二部分是年份国家和剧种, 就本文,我们需要的是第二部分的内容,也就是“1994 / 美国 / 犯罪 剧情”这部分内容。然后呢,因为整个content 包含了25部电影的文本, 这些都保存在列表里,并且我们需要进一步处理的,都在对应坐标1、3、5、、这样的奇数里。因为列表第一个是从0开始的。这就是第一个for循环的意义所在,用到了python的切片知识第二个冒号,表示从下表1的元素开始检索,并且隔开2个元素为下一次检索:
for i in content[1::2]:
查看了循环内的第一句为,注意这只是print,没有运行具体的代码影响原来的字符串:
print(str(i).strip().replace('\n\r', ''))
这句的意思就是得到整个content列表中,下标为1、3、5。。。。的元素,并通过strip()来进行去掉首尾的多余的空格,,随后使用replace函数,把’\n\r’替换为空字符。我们可以在ipython中验证下:
记录:aa = conntent[1]:
aa = '\n 1994\xa0/\xa0美国\xa0/\xa0犯罪 剧情\n '
执行:aa.strip(),得到:
In [19]: aa.strip()
Out[19]: '1994\xa0/\xa0美国\xa0/\xa0犯罪 剧情'
In [20]: aa.strip().replace('\n\r', '')
Out[20]: '1994\xa0/\xa0美国\xa0/\xa0犯罪 剧情'
发现上述的replace函数似乎没起作用,那是这里正好没有’\n\r’,同时你可以发现,这里怎么有4个地方有\xa0 ? 这是什么呢? 原来是:转义字符,”\x”后接数字(两位)代表16进制数,这玩意牵涉编码的问题,打印的时候是一个空格。
然后循环里面的开始执行的命令都是为了去除\n和空格这些没有的东西,i = str(i).split(‘/‘)就是把去除空格和\n的字符串通过‘/’分隔在列表里。
In [35]: aa = '\n 1994\xa0/\xa0美国\xa0/\xa0犯罪 剧情\n '
In [36]: aa = aa.split('/')
In [37]: aa = aa(len(aa)-1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-37-06f75198160a> in <module>()
----> 1 aa = aa(len(aa)-1)
TypeError: 'list' object is not callable
In [38]: aa = aa[len(aa)-1]
In [39]: aa
Out[39]: '\xa0犯罪 剧情\n
随后,通过
In [40]: key = aa.strip().replace('\n', '').split(' ')
In [41]: key
Out[41]: ['犯罪', '剧情']
发现,已经把\xa0当作左侧的空格给替换掉了,实际他显示出来就是一个空格的。然后通过中间的空格再劈开,得到了2个元素的字符串的列表。
这样就清洗出了所需要的数据。
下面的代码紧跟了一个针对key的for循环,是用来构建一个名为douban的字典dict,后续会把他的值再导入到数据库中。
for i in key:
if i not in douban.keys():
douban[i] = 1
else:
douban[i] += 1
里面的含义是:如果一个电影的分类关键词,在douban字典中(此时数据还没导入到数据库)不存在,那么新建一个对应名字的键值(keys),数值为1.否则如果是已经存在的,那么累加1.
5、 第二个函数是def save_mysql():作用是把提取的数据加载到数据库中去。
其中insert的那一行命令写的有点个性,但因为用到了过多的引号,所以我不是很推荐,一般的写法是: %s + 变量名
6、最后一个函数是用来画图的, 这块后续要配合nunpy,pandas等一直在加强下学习。
统计结果显示,绝大部分的好电影都是通过剧情抓住人心的:
7、 参考: 爬取豆瓣电影top250提取电影分类进行数据分析