批量爬取豆瓣影视短评步骤

Python013

批量爬取豆瓣影视短评步骤,第1张

用Python批量爬取豆瓣影视短评步骤:

1、通过Chrome浏览器检查元素。

2、获取单个页面HTML文本。

3、用正则表达式解析出所需要的信息并存入列表。

4、将列表中的信息存成csv文件。

5、利用start参数批量爬取其他页的短评。

这两天爬了豆瓣读书的十万条左右的书目信息,用时将近一天,现在趁着这个空闲把代码总结一下,还是菜鸟,都是用的最简单最笨的方法,还请路过的大神不吝赐教。

第一步,先看一下我们需要的库:

import requests                       #用来请求网页

from bs4 import BeautifulSoup         #解析网页

import time          #设置延时时间,防止爬取过于频繁被封IP号

import re            #正则表达式库

import pymysql       #由于爬取的数据太多,我们要把他存入MySQL数据库中,这个库用于连接数据库

import random        #这个库里用到了产生随机数的randint函数,和上面的time搭配,使爬取间隔时间随机

这个是豆瓣的网址:x-sorttags-all

我们要从这里获取所有分类的标签链接,进一步去爬取里面的信息,代码先贴上来:

import requests

from bs4 import BeautifulSoup       #导入库

url="httom/tag/?icn=index-nav"

wb_data=requests.get(url)                #请求网址

soup=BeautifulSoup(wb_data.text,"lxml")  #解析网页信息

tags=soup.select("#content >div >div.article >div >div >table >tbody >tr >td >a")

#根据CSS路径查找标签信息,CSS路径获取方法,右键-检查-copy selector,tags返回的是一个列表

for tag in tags:

tag=tag.get_text()    #将列表中的每一个标签信息提取出来

helf="hom/tag/"

#观察一下豆瓣的网址,基本都是这部分加上标签信息,所以我们要组装网址,用于爬取标签详情页

url=helf+str(tag)

print(url)    #网址组装完毕,输出

以上我们便爬取了所有标签下的网址,我们将这个文件命名为channel,并在channel中创建一个channel字符串,放上我们所有爬取的网址信息,等下爬取详情页的时候直接从这里提取链接就好了,如下:

channel='''

tag/程序

'''

现在,我们开始第二个程序。

QQ图片20160915233329.png

标签页下每一个图片的信息基本都是这样的,我们可以直接从这里提取到标题,作者,出版社,出版时间,价格,评价人数,以及评分等信息(有些外国作品还会有译者信息),提取方法与提取标签类似,也是根据CSS路径提取。

我们先用一个网址来实验爬取:

url="htt/tag/科技"

wb_data = requests.get(url)

soup = BeautifulSoup(wb_data.text.encode("utf-8"), "lxml")

tag=url.split("?")[0].split("/")[-1]    #从链接里面提取标签信息,方便存储

detils=soup.select("#subject_list >ul >li >div.info >div.pub")  #抓取作者,出版社信息,稍后我们用spite()函数再将他们分离出来

scors=soup.select("#subject_list >ul >li >div.info >div.star.clearfix >span.rating_nums")   #抓取评分信息

persons=soup.select("#subject_list >ul >li >div.info >div.star.clearfix >span.pl")    #评价人数

titles=soup.select("#subject_list >ul >li >div.info >h2 >a")   #书名

#以上抓取的都是我们需要的html语言标签信息,我们还需要将他们一一分离出来

for detil,scor,person,title in zip(detils,scors,persons,titles):

#用一个zip()函数实现一次遍历

#因为一些标签中有译者信息,一些标签中没有,为避免错误,所以我们要用一个try来把他们分开执行

try:

author=detil.get_text().split("/",4)[0].split()[0]     #这是含有译者信息的提取办法,根据“/”  把标签分为五部分,然后依次提取出来

yizhe= detil.get_text().split("/", 4)[1]

publish=detil.get_text().split("/", 4)[2]

time=detil.get_text().split("/", 4)[3].split()[0].split("-")[0]   #时间我们只提取了出版年份

price=ceshi_priceone(detil)        #因为价格的单位不统一,我们用一个函数把他们换算为“元”

scoe=scor.get_text() if True else ""    #有些书目是没有评分的,为避免错误,我们把没有评分的信息设置为空

person=ceshi_person(person)      #有些书目的评价人数显示少于十人,爬取过程中会出现错误,用一个函数来处理

title=title.get_text().split()[0]  

#当没有译者信息时,会显示IndexError,我们分开处理

except IndexError:

try:

author=detil.get_text().split("/", 3)[0].split()[0]

yizhe=""         #将detil信息划分为4部分提取,译者信息直接设置为空,其他与上面一样

publish=detil.get_text().split("/", 3)[1]

time=detil.get_text().split("/", 3)[2].split()[0].split("-")[0]

price=ceshi_pricetwo(detil)

scoe=scor.get_text() if True else ""

person=ceshi_person(person)

title=title.get_text().split()[0]

except (IndexError,TypeError):

continue  

#出现其他错误信息,忽略,继续执行(有些书目信息下会没有出版社或者出版年份,但是数量很少,不影响我们大规模爬取,所以直接忽略)

except TypeError:

continue

#提取评价人数的函数,如果评价人数少于十人,按十人处理

def ceshi_person(person):

try:

person = int(person.get_text().split()[0][1:len(person.get_text().split()[0]) - 4])

except ValueError:

person = int(10)

return person

#分情况提取价格的函数,用正则表达式找到含有特殊字符的信息,并换算为“元”

def ceshi_priceone(price):

price = detil.get_text().split("/", 4)[4].split()

if re.match("USD", price[0]):

price = float(price[1]) * 6

elif re.match("CNY", price[0]):

price = price[1]

elif re.match("\A$", price[0]):

price = float(price[1:len(price)]) * 6

else:

price = price[0]

return price

def ceshi_pricetwo(price):

price = detil.get_text().split("/", 3)[3].split()

if re.match("USD", price[0]):

price = float(price[1]) * 6

elif re.match("CNY", price[0]):

price = price[1]

elif re.match("\A$", price[0]):

price = float(price[1:len(price)]) * 6

else:

price = price[0]

return price

实验成功后,我们就可以爬取数据并导入到数据库中了,以下为全部源码,特殊情况会用注释一一说明。

import requests

from bs4 import BeautifulSoup

import time

import re

import pymysql

from channel import channel   #这是我们第一个程序爬取的链接信息

import random

def ceshi_person(person):

try:

person = int(person.get_text().split()[0][1:len(person.get_text().split()[0]) - 4])

except ValueError:

person = int(10)

return person

def ceshi_priceone(price):

price = detil.get_text().split("/", 4)[4].split()

if re.match("USD", price[0]):

price = float(price[1]) * 6

elif re.match("CNY", price[0]):

price = price[1]

elif re.match("\A$", price[0]):

price = float(price[1:len(price)]) * 6

else:

price = price[0]

return price

def ceshi_pricetwo(price):

price = detil.get_text().split("/", 3)[3].split()

if re.match("USD", price[0]):

price = float(price[1]) * 6

elif re.match("CNY", price[0]):

price = price[1]

elif re.match("\A$", price[0]):

price = float(price[1:len(price)]) * 6

else:

price = price[0]

return price

#这是上面的那个测试函数,我们把它放在主函数中

def mains(url):

wb_data = requests.get(url)

soup = BeautifulSoup(wb_data.text.encode("utf-8"), "lxml")

tag=url.split("?")[0].split("/")[-1]

detils=soup.select("#subject_list >ul >li >div.info >div.pub")

scors=soup.select("#subject_list >ul >li >div.info >div.star.clearfix >span.rating_nums")

persons=soup.select("#subject_list >ul >li >div.info >div.star.clearfix >span.pl")

titles=soup.select("#subject_list >ul >li >div.info >h2 >a")

for detil,scor,person,title in zip(detils,scors,persons,titles):

l = []  #建一个列表,用于存放数据

try:

author=detil.get_text().split("/",4)[0].split()[0]

yizhe= detil.get_text().split("/", 4)[1]

publish=detil.get_text().split("/", 4)[2]

time=detil.get_text().split("/", 4)[3].split()[0].split("-")[0]

price=ceshi_priceone(detil)

scoe=scor.get_text() if True else ""

person=ceshi_person(person)

title=title.get_text().split()[0]

except IndexError:

try:

author=detil.get_text().split("/", 3)[0].split()[0]

yizhe=""

publish=detil.get_text().split("/", 3)[1]

time=detil.get_text().split("/", 3)[2].split()[0].split("-")[0]

price=ceshi_pricetwo(detil)

scoe=scor.get_text() if True else ""

person=ceshi_person(person)

title=title.get_text().split()[0]

except (IndexError,TypeError):

continue  

except TypeError:

continue

l.append([title,scoe,author,price,time,publish,person,yizhe,tag])

#将爬取的数据依次填入列表中

sql="INSERT INTO allbooks values(%s,%s,%s,%s,%s,%s,%s,%s,%s)"  #这是一条sql插入语句

cur.executemany(sql,l)   #执行sql语句,并用executemary()函数批量插入数据库中

conn.commit()

#主函数到此结束

# 将Python连接到MySQL中的python数据库中

conn = pymysql.connect( user="root",password="123123",database="python",charset='utf8')

cur = conn.cursor()

cur.execute('DROP TABLE IF EXISTS allbooks')   #如果数据库中有allbooks的数据库则删除

sql = """CREATE TABLE allbooks(

title CHAR(255) NOT NULL,

scor CHAR(255),

author CHAR(255),

price CHAR(255),

time CHAR(255),

publish CHAR(255),

person CHAR(255),

yizhe CHAR(255),

tag CHAR(255)

)"""

cur.execute(sql)  #执行sql语句,新建一个allbooks的数据库

start = time.clock()   #设置一个时钟,这样我们就能知道我们爬取了多长时间了

for urls in channel.split():

urlss=[urls+"?start={}&type=T".format(str(i)) for i in range(0,980,20)]   #从channel中提取url信息,并组装成每一页的链接

for url in urlss:

mains(url)       #执行主函数,开始爬取

print(url)        #输出要爬取的链接,这样我们就能知道爬到哪了,发生错误也好处理

time.sleep(int(format(random.randint(0,9))))   #设置一个随机数时间,每爬一个网页可以随机的停一段时间,防止IP被封

end = time.clock()

print('Time Usage:', end - start)    #爬取结束,输出爬取时间

count = cur.execute('select * from allbooks')

print('has %s record' % count)       #输出爬取的总数目条数

# 释放数据连接

if cur:

cur.close()

if conn:

conn.close()

这样,一个程序就算完成了,豆瓣的书目信息就一条条地写进了我们的数据库中,当然,在爬取的过程中,也遇到了很多问题,比如标题返回的信息拆分后中会有空格,写入数据库中会出现错误,所以只截取了标题的第一部分,因而导致数据库中的一些书名不完整,过往的大神如果有什么办法,还请指教一二。

等待爬取的过程是漫长而又欣喜的,看着电脑上一条条信息被刷出来,成就感就不知不觉涌上心头;然而如果你吃饭时它在爬,你上厕所时它在爬,你都已经爬了个山回来了它还在爬时,便会有点崩溃了,担心电脑随时都会坏掉(还是穷学生换不起啊啊啊啊~)

所以,还是要好好学学设置断点,多线程,以及正则,路漫漫其修远兮,吾将上下而求索~共勉~

爬取时间:2020/11/25

系统环境:Windows 10

所用工具:Jupyter Notebook\Python 3.0

涉及的库:requests\lxml\pandas\matplotlib\numpy

蛋肥想法: 先将电影名称、原名、评分、评价人数、分类信息从网站上爬取下来。

蛋肥想法: print数据列表后发现电影原名、分类信息等存在不需要的字符,需预先处理;同时因为后续想做一个豆瓣电影TOP250的维度分布图,而同一电影存在多个发行国家、类型(如“法国 美国 / 剧情 动作 犯罪”),为了简(偷)便(懒),这里均取第一个作为记入的数据;最后将数据保存为xlsx。

蛋肥想法: 蛋肥想知道在豆瓣电影TOP250中年份、国家、类型的维度数据,为了练手,使用刚才保存成xlsx的数据,并分别画成雷达图、柱形图、扇形图。