手把手教你入门Python爬虫
前言
在上一篇文章中,作者带领大家完成了爬取中国银行外汇牌价的项目,相信大家已经对爬取静态网页的步骤有了一定的理解。今天,作者将带领大家完成爬取“微博电影信息”的项目。本次项目在数据处理上,与上一个项目略有不同,作者希望可以借此项目启发读者。
爬取电影名称这个项目不仅是为了学习爬虫框架,其在企业项目中也有一定的实际意义。例如social listening项目中的一个典型例子:国内电影市场环境分析中,我们首先要做的,就是获取近2-3年内上映的所有电影,存入全量库。以供后期借助电影名称,去社交网站爬取自然语言数据(如观后感等),进行情感分析等。
1. 观察网页,找寻规律
本次我们要爬取的网页网址为:
http://ent.sina.com.cn/ku/movie_search_index.d.html
点开网页链接后,可以看到网页的设计如下:
经分析发现,本次项目需要的信息为每一部电影图片旁的电影名,而每一页所显示的电影具体是什么,则是由“电影”字样下的筛选框决定的。我们选中2020年7月。点击确定后的页面如下:
此时,网页链接变为:
http://ent.sina.com.cn/ku/movie_search_index.d.html?years=2020-07
与原始网页链接相比,可以发现此时链接中多了一个参数years=2020-07,可以推断出,该参数是用来告诉微博后台服务器,我们请求的是哪一年几月份的电影榜单。(读者可以再另行选择其他月份,观察此规律)。
将页面拉至最后一页,可以发现2020-07的电影榜单被分成了6页,我们点击下一页。等待页面跳转后,可以发现网页URL变为:
http://ent.sina.com.cn/ku/movie_search_index.d.html?years=2020-07&page=2
对比可发现,此时网页链接又多了一个参数page=2(读者可以尝试着把page参数调为1,页面将自动跳转到2020-07的第一页)。根据我们学习过的知识,此时我们可以提炼出一个baseurl:http://ent.sina.com.cn/ku/movie_search_index.d.html?years=[年份-月参数]&page=[页码参数]。
此时如果读者仔细观察下页面所展示的每一部电影,可以发现:部分电影因为篇名过长,所以只显示了前8个字符。此时我们检查网页源码,可以发现,在网页的CSS样式中,也是显示不全的电影名。因此我们改变策略,将本项目分为两大块:
- 爬取指定日期内的全部电影的URL,存入一个表(全量表)
- 爬取每个URL指向的网页的电影名称(本部分将在下一期介绍)
2.爬取指定日期内的全部电影URL,存入表
(1)第一步,加载我们后期爬虫会用到的包
import bs4 # 网页解析,获得数据
import re # 正则表达式,进行文字匹配
import urllib.request,urllib.error # 指定URL,获取网页数据
import xlwt # 进行excel操作
from bs4 import BeautifulSoup
(2)第二步,分析输入输出
'''
@kris_luo
输入:指定格式的开始日期和结束日期,例如2020-7、2019-12:代表采集2020-7至2019-12之间的数据
输出:微博电影库中,开始日期和结束日期之间的所有电影URL
编写程序时要注意的点:
1. 考虑如何获得每一个月的榜单共有多少页
2. 每个月的榜单内电影数量不同,在存入excel时如何解决
'''
(3)给出baseurl(全局变量)和定义函数用于获取每一个月份榜单的URL
baseurl_1 = 'http://ent.sina.com.cn/ku/movie_search_index.d.html?years='
baseurl_2 = '&page=1'
begindate = '2020-7' #指定格式的开始日期
enddate = '2019-12' #指定格式的结束日期,enduretime>=1
def getUrl(date_start,date_end,base1,base2):
datelist = [] # 用于保存日期
urllist = [] # 用于保存url
get_date_1 = date_start.split('-')
year = get_date_1[0]
end_year = int(year) # 结束年份 2020
month = get_date_1[1]
end_month = int(month) # 结束月份 7
get_date_2 = date_end.split('-')
year = get_date_2[0]
start_year = int(year) # 开始年份 2019
month = get_date_2[1]
start_month = int(month) # 开始月份 12
str_date = str(start_year) + '-' + str(start_month)
datelist.append(str_date)
flag = 1
while flag:
# 若年份、月份相同,则退出循环
if((end_year == start_year) and (end_month == start_month)):
flag = 0
break
while start_month < 12:
start_month = start_month + 1
str_date = str(start_year) + '-' + str(start_month)
datelist.append(str_date)
if ((end_year == start_year) and (end_month == start_month)):
flag = 0
break
start_year = start_year + 1
start_month = 0
for i in datelist:
urllist.append(base1+i+base2)
return urllist
if __name__ == "__main__":
url_all = getUrl(begindate,enddate,baseurl_1,baseurl_2)
若执行print(url_all),可得到以下结果:
我们随意复制一个url,在浏览器打开,能够成功访问,该模块编写成功。
同时可以发现,日期2020-07与2020-7可以达到相同的访问页面。
(4) 根据url_all中的url访问页面,爬取指定月份的所有电影链接
在第四步,我们首先要根据每一个url获取到总页数信息,之后循环总页数次修改url实现翻页操作。
依据我们已有的知识,通过设置soup.findall()方法中参数为:
page_num = soup.find_all('div', class_='to-page')
进行页码标签定位。定位结果如下:
之后我们设置正则表达式,取出数字6:
# 定义正则表达式用于获取总页数
findpagenum = re.compile(r'<div class="to-page"><span>共(.*?)页</span></div>')
def getData(baseurl):
html = askURL(baseurl) # 保存获取到的网页源码,即每个月份电影榜单的第一页
soup = BeautifulSoup(html, 'html.parser') # 解析网页
page_num = soup.find_all('div', class_='to-page') # 输出结果大致为[<div class="to-page"><span>共6页</span></div>]
page_num = str(page_num)
page_number = re.findall(findpagenum,page_num)
return page_number
运行程序,可得page_number为:
接下来,我们需要根据page_number的取值,生成page_number个url,代表确定月份榜单内的所有网页链接。
# 爬取网页,用于获取数据
def getData(baseurl):
film_url_list = [] # 用于保存电影链接
filmlinklist = []
html = askURL(baseurl) # 保存获取到的网页源码,即每个月份电影榜单的第一页
soup = BeautifulSoup(html, 'html.parser') # 解析网页
page_num = soup.find_all('div', class_='to-page') # 输出结果大致为[<div class="to-page"><span>共6页</span></div>]
page_num = str(page_num)
page_number = re.findall(findpagenum,page_num)
page_number = page_number[0]
page_number = int(page_number)
i = 1
month_url = baseurl[0:-1] # 去掉页码1
while i <= page_number:
page_url = month_url + str(i)
i = i + 1
page_html = askURL(page_url) # 访问具体一页的url链接
page_soup = BeautifulSoup(page_html, 'html.parser') # 解析网页
film_url_1 = page_soup.find_all('a', class_='item-img left') # 找到这一页上所有电影链接
film_url_list.append(film_url_1)
for j in film_url_list:
filmurllist = str(j)
filmlink = re.findall(findfilmlink, filmurllist)
filmlinklist.append(filmlink)
return filmlinklist
我们将爬取到的页面url输出到控制台检查一下:
我们打开最后一个url检查一下,如果正确的话,应该是2020-07榜单中的最后一个电影:前哨
但是页面并不是电影《前哨》的详情页,反而是《人渣的本愿》。我们手动找到《前哨》的主页链接,与爬取到的主页链接对比下:
经对比可以发现,微博后台发给我们的链接中,塞入了四个字符amp;,只要任意链接加上这四个字符,都会被定位到电影《人渣的本愿》。说明我们的爬虫被微博识破了,并采取了反扒手段,不过这并不影响我们爬取电影信息数据。我们只需对链接进行处理,删去amp;即可得到正确的电影详情页url。
完整代码如下:
import bs4 # 网页解析,获得数据
import re # 正则表达式,进行文字匹配
import urllib.request,urllib.error # 指定URL,获取网页数据
import xlwt # 进行excel操作
from bs4 import BeautifulSoup
'''
@kris_luo
输入:指定格式的开始日期和结束日期,例如2020-7、2019-12:代表采集2020-7至2019-12之间的数据
输出:微博电影库中,开始日期和结束日期之间的所有电影URL
编写程序时要注意的点:
1. 考虑如何获得每一个月的榜单共有多少页
2. 每个月的榜单内电影数量不同,在存入excel时如何解决
'''
baseurl_1 = 'http://ent.sina.com.cn/ku/movie_search_index.d.html?years='
baseurl_2 = '&page=1'
begindate = '2020-7' #指定格式的开始日期
enddate = '2020-5' #指定格式的结束日期
findpagenum = re.compile(r'<div class="to-page"><span>共(.*?)页</span></div>') # 定义正则表达式用于获取总页数
findfilmlink = re.compile(r'<a class="item-img left" href="(.*?)" target="_blank" title="',re.S)
def getUrl(date_start,date_end,base1,base2):
datelist = [] # 用于保存日期
urllist = [] # 用于保存url
get_date_1 = date_start.split('-')
year = get_date_1[0]
end_year = int(year) # 结束年份 2020
month = get_date_1[1]
end_month = int(month) # 结束月份 7
get_date_2 = date_end.split('-')
year = get_date_2[0]
start_year = int(year) # 开始年份 2019
month = get_date_2[1]
start_month = int(month) # 开始月份 12
str_date = str(start_year) + '-' + str(start_month)
datelist.append(str_date)
flag = 1
while flag:
# 若年份、月份相同,则退出循环
if((end_year == start_year) and (end_month == start_month)):
flag = 0
break
while start_month < 12:
start_month = start_month + 1
str_date = str(start_year) + '-' + str(start_month)
datelist.append(str_date)
if ((end_year == start_year) and (end_month == start_month)):
flag = 0
break
start_year = start_year + 1
start_month = 0
for i in datelist:
urllist.append(base1+i+base2)
return urllist
# 得到指定一个URL的网页内容
def askURL(url):
head = { # 模拟浏览器头部信息
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
} # 用于告诉服务器,我们可以接收什么水平的文件内容(即伪装成浏览器)
request = urllib.request.Request(url, headers=head)
html = ''
try:
response = urllib.request.urlopen(request)
html = response.read().decode('utf-8')
except urllib.error.URLError as e:
if hasattr(e, 'code'):
print(e.code)
if hasattr(e, 'reason'):
print(e.reason)
return html
# 爬取网页,用于获取数据
def getData(baseurl):
film_url_list = [] # 用于保存电影链接
filmlinklist = []
html = askURL(baseurl) # 保存获取到的网页源码,即每个月份电影榜单的第一页
soup = BeautifulSoup(html, 'html.parser') # 解析网页
page_num = soup.find_all('div', class_='to-page') # 输出结果大致为[<div class="to-page"><span>共6页</span></div>]
page_num = str(page_num)
page_number = re.findall(findpagenum,page_num)
page_number = page_number[0]
page_number = int(page_number)
i = 1
month_url = baseurl[0:-1] # 去掉页码1
while i <= page_number:
page_url = month_url + str(i)
i = i + 1
page_html = askURL(page_url) # 访问具体一页的url链接
page_soup = BeautifulSoup(page_html, 'html.parser') # 解析网页
film_url_1 = page_soup.find_all('a', class_='item-img left') # 找到这一页上所有电影链接
film_url_list.append(film_url_1)
for j in film_url_list:
filmurllist = str(j)
filmlink = re.findall(findfilmlink, filmurllist)
filmlinklist.append(filmlink)
return filmlinklist
def saveData(datalist,savepath): # 保存数据,参数为保存路径
book = xlwt.Workbook(encoding='utf-8',style_compression=0)
sheet = book.add_sheet('电影链接', cell_overwrite_ok=True)
col = ('电影链接')
sheet.write(0,0,col)
k = 1
for i in datalist:
for j in i:
for q in j:
sheet.write(k, 0, q)
k = k + 1
book.save(savepath)
if __name__ == "__main__":
url_all = getUrl(begindate,enddate,baseurl_1,baseurl_2) # 指定日期内所有URL链接
all_film_link = []
for h in url_all:
getfilmlink = getData(h)
all_film_link.append(getfilmlink)
savepath = '电影链接.xls'
saveData(all_film_link, savepath)
运行后,我们查看保存的excel表,对A列进行去重操作,并验证是否正确:
共爬取到电影链接147条,通过查看原网站对比发现,数目匹配。
接下来通过excel的分列操作,去除掉==amp;==字符。
点开第一个链接,验证是否为2020-05榜单第一部电影:
通过验证。本项目的上半部分----爬取指定日期的电影详情页url便完成了。