手把手教你入门Python爬虫(三)

手把手教你入门Python爬虫

前言

  在上一篇文章中,作者带领大家完成了爬取中国银行外汇牌价的项目,相信大家已经对爬取静态网页的步骤有了一定的理解。今天,作者将带领大家完成爬取“微博电影信息”的项目。本次项目在数据处理上,与上一个项目略有不同,作者希望可以借此项目启发读者。
  爬取电影名称这个项目不仅是为了学习爬虫框架,其在企业项目中也有一定的实际意义。例如social listening项目中的一个典型例子:国内电影市场环境分析中,我们首先要做的,就是获取近2-3年内上映的所有电影,存入全量库。以供后期借助电影名称,去社交网站爬取自然语言数据(如观后感等),进行情感分析等。

1. 观察网页,找寻规律

  本次我们要爬取的网页网址为:

http://ent.sina.com.cn/ku/movie_search_index.d.html

  点开网页链接后,可以看到网页的设计如下:
微博电影互动资料库
  经分析发现,本次项目需要的信息为每一部电影图片旁的电影名,而每一页所显示的电影具体是什么,则是由“电影”字样下的筛选框决定的。我们选中2020年7月。点击确定后的页面如下:
2020年7月电影榜单
  此时,网页链接变为:

http://ent.sina.com.cn/ku/movie_search_index.d.html?years=2020-07

  与原始网页链接相比,可以发现此时链接中多了一个参数years=2020-07,可以推断出,该参数是用来告诉微博后台服务器,我们请求的是哪一年几月份的电影榜单。(读者可以再另行选择其他月份,观察此规律)。
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

  • 爬取每个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
  我们随意复制一个url,在浏览器打开,能够成功访问,该模块编写成功。
验证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的取值,生成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
  我们打开最后一个url检查一下,如果正确的话,应该是2020-07榜单中的最后一个电影:前哨
检查url
  但是页面并不是电影《前哨》的详情页,反而是《人渣的本愿》。我们手动找到《前哨》的主页链接,与爬取到的主页链接对比下:
《前哨》链接对比
  经对比可以发现,微博后台发给我们的链接中,塞入了四个字符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便完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值