爬取豆瓣电影 Top-250 到 excel 表格中
最近在家无聊自学了python的一些基础知识。后来看到许多朋友都在写爬虫,自己感觉很有意思,也想试一下 >____<
其实本来我是想将数据爬取到excel之后再增加一些数据库操作,然后用flash建立一个网站将数据导入这个网站中实现数据可视化来着… 但!!!当我万分激动地上号我的pycharm,使用pip安装sqlite3之后,发现我的pycharm无法进行SQLite数据库操作,如图:
但是导入模块时,模块名并没有红色下划线,说明导入成功了啊!而且,当我用尝试用 IDLE shell 操作数据库时,试验了下并没有问题,sqlite3模块可用。这就很让人迷惑了哈~~~ 有图有真相:
下图是我用 IDLE shell 建立的数据库文件:
虽然使用 IDLE shell 可以进行 SQLite 数据库常规操作,但 shell 毕竟太过简陋,操作起来对我这种初学者极其极其的不友好!!!所以,我后面数据可视化的想法刚刚出生就被我的 pycharm 一屁股坐死了 … 0.0
**希望路过的大佬帮助小弟指点迷津 >_____<
分割线----------------------------------------------------------------------------------------------------------------------------
好了,下面是我用 shell 写的一个小爬虫(阉割版),仅仅进行了将相关信息爬取到 excel 中的操作。小白操作,大佬勿喷
首先,我们需要导入相关模块:
import re #正则表达式,进行文字匹配
from bs4 import BeautifulSoup #网页解析,获取数据
import sqlite3 #进行SQLite数据库操作
import xlwt #进行excel操作
import urllib.request, urllib.error #制定URL,获取网页数据
import os
import sys
当然,里面关于数据库的操作我没有用到,先贴在这里等以后搞明白问题出在哪里了再更新
爬取操作主要分为以下三个步骤:
得到一个指定URL网页的内容
爬取网页
保存数据
于是我定义了三个函数分别对这三个功能进行封装,我们一个一个来哈~~~
首先,是得到一个指定URL网页的内容
因为大多数网页并不欢迎爬虫访问自己,因为会占用网站很大的资源。所以,它们都具有反爬机制。但魔高一尺道高一丈,我们也有反反爬策略!!!(手动狗头)
反反爬豆瓣并不难,只要将我们的爬虫伪装成浏览器就好咯。那怎么伪装呢?很简单,将我们python访问器的名字改成浏览器的名字,再有模有样地将浏览器信息一本正经地写入。然后,对于豆瓣来说你就是那个最靓的浏览器!而不是一只讨厌的小虫子(小声bb)
那么接下来的工作就是获取我们的浏览器名以及其他信息了。获取方法并不复杂,只需要两个强大的工具:ctrl-c(复制),ctrl-v(粘贴)
首先我们需要打开我们的浏览器,找到豆瓣Top-250网址 https://movie.douban.com/top250 ,然后打开开发者模式(键盘按下F12),发现一大堆代码
点击 Network ,然后刷新界面(对,就是你经常用的那个刷新!),然后你会发现界面在动,立刻点击红色按钮暂停刷新。找到最前面的那条时间线,就是最前面单条的那条横线,点一下。你会发现出来了一个 top250 的文件,点击那个文件。啥,没太听懂?没事,咱直接上图:
点开 top250 文件后,你又会发现一些新按钮,点击 Headers,然后一直将页面拉到最底,将 User-Agent 那段话复制下来,留着用(成功一半了)
然后我们开始写代码。这是我第一个函数,用来得到一个指定URL网页的内容。得到内容并不难,主要是要破解豆瓣的反爬机制。
我们定义一个 head 变量,将刚才复制的浏览器信息以字典形式写入,注意!!!一定要是字典形式!
head = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'}
然后接下来就简单了,直接调用 urllib.request 模块进行基本的网页读取。在以下代码块中req是请求对象,respond是响应对象。前者负责对网页发出访问请求(带着伪装),后者负责接收网页发出的相应(我的个人理解,参考)。请求对象中的 req = urllib.request.Request(url, headers=head) 是对信息进行封装(依旧是我的个人理解,有哪里不对烦请路过的大佬指正,不胜感激)。参数 url 是要访问的网址。最后返回的 html 当然就是获取到的网页内容啦!
为了能够使程序能够自己消化一部分 BUG,这里加入了异常处理(try-except语句)。这样就不至于有时候报出一大堆红色异常(看着挺烦的)甚至直接导致程序崩溃了。
下面贴出第一个函数的完整代码:
#得到一个指定URL网页的内容
def askurl(url):
html = ''
try:
head = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'}
req = urllib.request.Request(url, headers=head)
respond = urllib.request.urlopen(req)
html = respond.read().decode('utf-8')
except urllib.error.HTTPError as e:
if hasattr(e, 'code'):
print(e.code)
if hasattr(e, 'reason'):
print(e.reason)
return html
然后,我们就要进行第二个操作,爬取网页啦!
这里要用到 bs4 里的 BeautifulSoup 模块,将 html 网页内容转化成一个复杂的树形结构。大家打开豆瓣网页就会发现,每一页只有25个电影信息,如果想要得到250个电影信息的话需要遍历十页。我们上一个函数只是获取了一个网页的内容,那么如何获取多个网页呢?很简单,for循环。
仔细观察我们会发现豆瓣每个页面的网址都是有规律的。比如第一页:https://movie.douban.com/top250,实际上它省略了一些东西,完整网址应该是https://movie.douban.com/top250?start=0 啥?你问我咋发现的??很简单,跳转到下一页比对一下嘛,很容易发现规律的。你会发现第二页就是https://movie.douban.com/top250?start=25,第三页是https://movie.douban.com/top250?start=50…很明显,每一页的网址最后的数字都是上一页最后电影的序号。知道了这些,我们就能用循环来访问10个网址啦(其实网址最后的数字可以是1~250任意一个,大家有兴趣可以试试。我们这里就用25个电影一组进行爬取咯,是不是很好玩很有意思嘿嘿)
for循环:
for i in range(10):
url = baseurl + str(i*25) #前面相同的 baseurl 在函数参数传入
html = askurl(url) #上一个定义的函数,获取网页内容
在for循环内部,获取网页内容后对每一个网页逐一进行解析。我们用 BeautifulSoup(html, ‘html.parser’)来完成这个工作。前一个参数html是被解析的网页对象,后一个参数是解析器名称,我们用 html.parser 这个解析器进行解析,并将解析结果赋值给 soup 对象。注意,此时 soup 存放的是一整个网页的内容,里面有25个电影的信息。所以我们还需要再嵌套一个循环,将25个电影逐一进行爬取。
#逐一解析数据
soup = BeautifulSoup(html, 'html.parser')
for item in soup.find_all('div', class_='item'):
data = []
我们来理一下思路:250部电影有10页,每页有25个电影,每个电影我们需要爬取8个标签(疯狂套娃)。哦?你问我啥是标签??不急,咱慢慢说
前面提过,BeautifulSoup 模块就是将 html 网页内容转化成一个复杂的树形结构。那么,有哪几种树形结构呢?主要有以下四种:
Tag 标签及其内容
Navigablestring #标签里的内容,字符串
BeautifulSoup #输出整个文档
comment #特殊的Navigablestring,输出的内容不包含注释符号
我们说的标签,一般指的就是Tag。当你打开浏览器的开发者模式后,点击小箭头,然后点击你想定位的内容,就能定位到相关标签。如图:
当你找到你想要定位的内容时,右键点击代码,点击弹出的 Edit as HTML,就能对其进行复制。我们以上图中的电影名标签为例:
<span class="title">肖申克的救赎</span>
其中 span 是标签名,class="title‘’ 是标签属性,‘’肖申克的救赎‘’ 是字符串内容。我们要提取的就是它的字符串内容(当然只是在这个标签中,其他标签中有可能提取属性或标签名)。至于如何提取,这要用到正则表达式的知识。正则表达式就是几个特定符号组合成的简单语法,用于字符串操作。这里不再赘述,自行百度(狗头)
至于我们上个代码块中的 for 循环(如下),soup.find_all(‘div’, class_=‘item’) 函数,就是用来查找所有标签。具体翻译一下就是:查找所有标签名为’div’,属性 class_=‘item’ 的标签,并返回一个列表(在豆瓣网站上定位一下就知道,这个标签下的子标签几乎涵盖了一个电影的全部内容,所以把它拿来用)。因为列表是一个可迭代对象,所以可以直接拿来循环。然后下文中的每个 item 就是一个电影的所有标签。
for item in soup.find_all('div', class_='item'):
我们需要对提取的字符串进行正则表达式语法定义,即设置模板。至于正则表达式语法,自行百度。下面是我进行的模板设置:
#影片详情链接
findLink = re.compile(r'<a href="(.*?)">')
#图片链接
findImage = re.compile(r'<img.*?src="(.*?)".*?>', re.S)
#评分
findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>')
#评价人数
findJudge = re.compile(r'<span>(.*)人评价</span>')
#标题
findTitle = re.compile(r'<span class="title">(.*?)</span>')
#一句话概述
findInq = re.compile(r'<span class="inq">(.*?)</span>',re.S)
#影片相关内容(导演、主演)
findBd = re.compile(r'<p class="">(.*?)</p>', re.S)
然后下面就是用正则表达式进行字符串提取。有时候提取完成后会出现一些奇奇怪怪的我们不想要的符号,用正则表达式或字符串内置的BIF替换掉就好咯。这里没什么可说的
item = str(item) #将每个电影信息转化成字符串,以进行正则表达式操作
link = re.findall(findLink, item)[0]
image = findImage.findall(item)[0]
title = findTitle.findall(item)
if len(title)==1:
title = [title[0], ' ']
rating = findRating.findall(item)[0]
judge = findJudge.findall(item)[0]
inq = findInq.findall(item)
if len(inq)==0:
inq = ' '
if len(inq)==1:
inq = inq[0]
bd = findBd.findall(item)[0]
bd = re.sub('<.*?>(\s+)', '', bd)
bd = re.sub('\xa0', '',bd)
bd = re.sub('/', '',bd)
bd = bd.strip()
title[1] = title[1].replace('\xa0/\xa0', '')
再然后,将每一个电影信息装入一个列表进行存储。然后再将这个列表作为一个元素装入一个大列表中,进行所有电影的汇总。这个函数的骚操作完成,输出一下提取到的内容:
芜湖!!!
贴出这个函数的完整代码:
#爬取网页
def getdata(baseurl):
datalist = []
for i in range(10):
url = baseurl + str(i*25)
html = askurl(url)
#逐一解析数据
soup = BeautifulSoup(html, 'html.parser')
for item in soup.find_all('div', class_='item'):
data = []
item = str(item)
link = re.findall(findLink, item)[0]
image = findImage.findall(item)[0]
title = findTitle.findall(item)
if len(title)==1:
title = [title[0], ' ']
rating = findRating.findall(item)[0]
judge = findJudge.findall(item)[0]
inq = findInq.findall(item)
if len(inq)==0:
inq = ' '
if len(inq)==1:
inq = inq[0]
bd = findBd.findall(item)[0]
bd = re.sub('<.*?>(\s+)', '', bd)
bd = re.sub('\xa0', '',bd)
bd = re.sub('/', '',bd)
bd = bd.strip()
title[1] = title[1].replace('\xa0/\xa0', '')
data.extend([link, image, title[0], title[1], rating, judge, inq, bd])
datalist.append(data)
return datalist
最后一个函数辣(芜湖,起飞!)
保存数据到 excel 表格中
在这里,我们要用到 xlwt 模块将数据写入excel
book是创建的一个 excel 文件,其中 encoding=‘utf-8’ 是编码格式。sheet 是在这个文件里创建的一个工作表,第一个参数是工作表的名字,第二个参数是规定覆盖式写入(参照文件操作w格式写入)
book = xlwt.Workbook(encoding='utf-8', style_compression=0)#样式压缩效果
sheet = book.add_sheet('豆瓣电影 Top-250 详情信息', cell_overwrite_ok=True)#写入时覆盖以前内容
然后在第一行写入各个内容的标题。其中,write()函数有三个参数,前两个是写入表格的位置(行,列),第三个是写入的内容。实质上,excel 表格就是一个巨大的矩阵,每个单元格都有它的坐标,从零开始。
col = ('电影详情链接', '图片链接', '电影中文名', '电影英文名', '豆瓣评分', '评价人数', '一句话概述', '影片相关内容')
for i in range(8):
sheet.write(0, i, col[i])
接下来就是将之前爬取的数据写入表格,循环即可:
for i in range(250):
print('第%d行打印' % (i+1))
for j in range(8):
sheet.write(i+1, j, datalist[i][j])
最后,文件保存,收工!!!
book.save(savepath)
return
下面是这个函数的完整代码:
#保存数据到 excel 表格中
def savedata(datalist, savepath):
book = xlwt.Workbook(encoding='utf-8', style_compression=0)#样式压缩效果
sheet = book.add_sheet('豆瓣电影 Top-250 详情信息', cell_overwrite_ok=True)#写入时覆盖以前内容
col = ('电影详情链接', '图片链接', '电影中文名', '电影英文名', '豆瓣评分', '评价人数', '一句话概述', '影片相关内容')
for i in range(8):
sheet.write(0, i, col[i])
for i in range(250):
print('第%d行打印' % (i+1))
for j in range(8):
sheet.write(i+1, j, datalist[i][j])
book.save(savepath)
return
看一下效果:
哈哈哈哈内心有那么一点小激动
整个程序的完整代码我贴在这里了,运行后你在D盘会发现一个文件叫做:
打开就可以看到电影信息。但你必须提前安装过相关模块,否则会报错。而且,要保证你的电脑有网
安装模块我建议 pip 安装。如果你已经安装过 pip,只需键盘按下 windows+r,然后在弹出框里输入 cmd,在弹出的黑框里输入 pip install [模块名称],然后静静等着就行了。安装完成后如果没法用,重启一下IDLE。
没安装 pip 的在网上自行百度安装,一劳永逸。当然,用 Anaconda 进行安装也是一个很好的选择(我感觉两者差不多…)
不说了,以下是完整代码:
#-*- coding = utf-8 -*-
#@Time : 2020/7/22 11:33
#@Author : 辉
#@File : 爬取豆瓣 TOP-250 电影.py
#@Software : IDLE shell
import re #正则表达式,进行文字匹配
from bs4 import BeautifulSoup #网页解析,获取数据
import sqlite3 #进行SQLite数据库操作
import xlwt #进行excel操作
import urllib.request, urllib.error #制定URL,获取网页数据
import os
import sys
#得到一个指定URL网页的内容
def askurl(url):
html = ''
try:
head = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'}
req = urllib.request.Request(url, headers=head)
respond = urllib.request.urlopen(req)#注意括号里不要写成url,犯了两次了
html = respond.read().decode('utf-8')
except urllib.error.HTTPError as e:
if hasattr(e, 'code'):
print(e.code)
if hasattr(e, 'reason'):
print(e.reason)
return html
#影片详情链接
findLink = re.compile(r'<a href="(.*?)">')
#图片链接
findImage = re.compile(r'<img.*?src="(.*?)".*?>', re.S)
#评分
findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>')
#评价人数
findJudge = re.compile(r'<span>(.*)人评价</span>')
#标题
findTitle = re.compile(r'<span class="title">(.*?)</span>')
#一句话概述
findInq = re.compile(r'<span class="inq">(.*?)</span>',re.S)
#影片相关内容(导演、主演)
findBd = re.compile(r'<p class="">(.*?)</p>', re.S)
#爬取网页
def getdata(baseurl):
datalist = []
for i in range(10):
url = baseurl + str(i*25)
html = askurl(url)
#逐一解析数据
soup = BeautifulSoup(html, 'html.parser')
for item in soup.find_all('div', class_='item'):
data = []
item = str(item)
link = re.findall(findLink, item)[0]
image = findImage.findall(item)[0]
title = findTitle.findall(item)
if len(title)==1:
title = [title[0], ' ']
rating = findRating.findall(item)[0]
judge = findJudge.findall(item)[0]
inq = findInq.findall(item)
if len(inq)==0:
inq = ' '
if len(inq)==1:
inq = inq[0]
bd = findBd.findall(item)[0]
bd = re.sub('<.*?>(\s+)', '', bd)
bd = re.sub('\xa0', '',bd)
bd = re.sub('/', '',bd)
bd = bd.strip()
title[1] = title[1].replace('\xa0/\xa0', '')
data.extend([link, image, title[0], title[1], rating, judge, inq, bd])
datalist.append(data)
return datalist
#保存数据
def savedata(datalist, savepath):
book = xlwt.Workbook(encoding='utf-8', style_compression=0)#样式压缩效果
sheet = book.add_sheet('豆瓣电影 Top-250 详情信息', cell_overwrite_ok=True)#写入时覆盖以前内容
col = ('电影详情链接', '图片链接', '电影中文名', '电影英文名', '豆瓣评分', '评价人数', '一句话概述', '影片相关内容')
for i in range(8):
sheet.write(0, i, col[i])
for i in range(250):
print('第%d行打印' % (i+1))
for j in range(8):
sheet.write(i+1, j, datalist[i][j])
book.save(savepath)
return
def main():
data = getdata('https://movie.douban.com/top250?start=')
savedata(data, 'd:\\豆瓣电影 Top-250 详情信息.xls')
if __name__ == '__main__':
main()
溜了溜了,吃饭去了(手动狗头)
博文不当之处还请路过的大佬批评指正,不胜感激!!!
本文地址:https://blog.csdn.net/qq_45734879/article/details/107629592