爬蟲---實現爬取 電影資料和電影評論(豆瓣)

效果圖:

在這裏插入圖片描述

爬取電影資料 源代碼:

import requests
from lxml import etree
import json
import csv
import time
import random

# 獲取網頁源代碼
def get_page(url):
    headers = {
        'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    response = requests.get(url=url,headers=headers)
    html = response.text
    return html

# 解析網頁源代碼
def parse_page(html):
    html_elem = etree.HTML(html)
    links = html_elem.xpath('//div[@class="hd"]/a/@href')
    titles = html_elem.xpath('//div[@class="hd"]/a/span[1]/text()')
    infos = html_elem.xpath('//div[@class="bd"]/p[1]//text()')
    roles = [j.strip() for i,j in enumerate(infos) if i % 2 == 0]
    descritions = [j.strip() for i,j in enumerate(infos) if i % 2 != 0]
    stars = html_elem.xpath('//div[@class="bd"]/div/span[2]/text()')
    comments = html_elem.xpath('//div[@class="bd"]/div/span[4]/text()')
    data = zip(links,titles,roles,descritions,stars,comments)
    return data

# 打開文件
def openfile(fm):
    fd = None
    if fm == 'txt':
        fd = open('douban.txt','w',encoding='utf-8')
    elif fm == 'json':
        fd = open('douban.json','w',encoding='utf-8')
    elif fm == 'csv':
        fd = open('douban.csv','w',encoding='utf-8',newline='')
    return fd

# 將數據保存到文件
def save2file(fm,fd,data):
    if fm == 'txt':
        for item in data:
            fd.write('----------------------------------------\n')
            fd.write('link:' + str(item[0]) + '\n')
            fd.write('title:' + str(item[1]) + '\n')
            fd.write('role:' + str(item[2]) + '\n')
            fd.write('descrition:' + str(item[3]) + '\n')
            fd.write('star:' + str(item[4]) + '\n')
            fd.write('comment:' + str(item[5]) + '\n')
    if fm == 'json':
        temp = ('link','title','role','descrition','star','comment')
        for item in data:
            json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
    if fm == 'csv':
        writer = csv.writer(fd)
        for item in data:
            writer.writerow(item)

# 開始爬取網頁
def crawl():
    url = 'https://movie.douban.com/top250?start={page}&filter='
    fm = input('請輸入文件保存格式(txt、json、csv):')
    while fm!='txt' and fm!='json' and fm!='csv':
        fm = input('輸入錯誤,請重新輸入文件保存格式(txt、json、csv):')
    fd = openfile(fm)
    print('開始爬取')
    for page in range(0,250,25):  #一次加 25頁
        print('正在爬取第 ' + str(page+1) + ' 頁至第 ' + str(page+25) + ' 頁......')
        html = get_page(url.format(page=str(page)))
        data = parse_page(html)
        save2file(fm,fd,data)
        time.sleep(random.random())
    fd.close()
    print('結束爬取')

if __name__ == '__main__':
    crawl()

爬取電影評論 源代碼:

import requests
from lxml import etree
import re
import json
import csv
import time
import random

# 獲取網頁源代碼
def get_page(url):
    headers = {
        'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    response = requests.get(url=url,headers=headers)
    html = response.text
    return html

# 解析網頁源代碼,獲取下一頁鏈接
def parse4link(html,base_url):
    link = None
    html_elem = etree.HTML(html)
    url = html_elem.xpath('//div[@id="paginator"]/a[@class="next"]/@href')
    if url:
        link = base_url + url[0]
    return link

# 解析網頁源代碼,獲取數據
def parse4data(html):
    html = etree.HTML(html)
    agrees = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[1]/span/text()')
    authods = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[2]/a/text()')
    stars = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[2]/span[2]/@title')
    contents = html.xpath('//div[@class="comment-item"]/div[2]/p/span/text()')
    data = zip(agrees,authods,stars,contents)
    return data

# 打開文件
def openfile(fm):
    fd = None
    if fm == 'txt':
        fd = open('douban_comment.txt','w',encoding='utf-8')
    elif fm == 'json':
        fd = open('douban_comment.json','w',encoding='utf-8')
    elif fm == 'csv':
        fd = open('douban_comment.csv','w',encoding='utf-8',newline='')
    return fd

# 將數據保存到文件
def save2file(fm,fd,data):
    if fm == 'txt':
        for item in data:
            fd.write('----------------------------------------\n')
            fd.write('agree:' + str(item[0]) + '\n')
            fd.write('authod:' + str(item[1]) + '\n')
            fd.write('star:' + str(item[2]) + '\n')
            fd.write('content:' + str(item[3]) + '\n')
    if fm == 'json':
        temp = ('agree','authod','star','content')
        for item in data:
            json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
    if fm == 'csv':
        writer = csv.writer(fd)
        for item in data:
            writer.writerow(item)

# 開始爬取網頁
def crawl():
    moveID = input('請輸入電影ID:')
    while not re.match(r'\d{8}',moveID):  #正則表達式驗證是否輸入正確
        moveID = input('輸入錯誤,請重新輸入電影ID:')
    base_url = 'https://movie.douban.com/subject/' + moveID + '/comments'
    fm = input('請輸入文件保存格式(txt、json、csv):')
    while fm!='txt' and fm!='json' and fm!='csv':
        fm = input('輸入錯誤,請重新輸入文件保存格式(txt、json、csv):')
    fd = openfile(fm)
    print('開始爬取')
    link = base_url
    while link:
        print('正在爬取 ' + str(link) + ' ......')
        html = get_page(link)
        link = parse4link(html,base_url)
        data = parse4data(html)
        save2file(fm,fd,data)
        time.sleep(random.random())
    fd.close()
    print('結束爬取')

if __name__ == '__main__':
    crawl()

講解:

一、requests 庫

相比 python 自帶的 urllib 庫,需要下載的 requests 庫功能強大、簡單易用
(注意:別和 urllib 庫的 request 模塊的 Request 方法搞混淆)

更方便的獲取網頁源代碼
def get_page(url):
    headers = {
        'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    response = requests.get(url=url,headers=headers)
    html = response.text
    return html

用到了 get()text() 函數,獲取網頁源代碼,代碼長度與之前相比明顯更短了(函數不解析,明白在哪用即可)

二、lxml 庫的 etree 模塊

1、.HTML 方法
構造 lxml.etree._Element 對象,爲後面的方法做準備

2、.xpath 方法(另類的正則表達式)
豆瓣網頁源代碼中的一段:
在這裏插入圖片描述

def parse_page(html):
    # 構造 _Element 對象
    html_elem = etree.HTML(html)
    # 詳細鏈接
    links = html_elem.xpath('//div[@class="hd"]/a/@href')
    # 電影名稱
    titles = html_elem.xpath('//div[@class="hd"]/a/span[1]/text()')
    # 電影信息(導演/主演、上映年份/國家/分類)
    infos = html_elem.xpath('//div[@class="bd"]/p[1]//text()')
    # 電影導演 電影描述 (詳細見最下面)
    roles = [j.strip() for i,j in enumerate(infos) if i % 2 == 0]
    descritions = [j.strip() for i,j in enumerate(infos) if i % 2 != 0]
    # 豆瓣評分
    stars = html_elem.xpath('//div[@class="bd"]/div/span[2]/text()')
    # 評論人數
    comments = html_elem.xpath('//div[@class="bd"]/div/span[4]/text()')
    # 獲得結果
    data = zip(links,titles,roles,descritions,stars,comments)
    # 返回結果
    return data

相關匹配語法可以跳轉到結尾的博客參考,下面重點講講這兩個句子

roles = [j.strip() for i,j in enumerate(infos) if i % 2 == 0]
descritions = [j.strip() for i,j in enumerate(infos) if i % 2 != 0]

strip() 方法用於移除字符串頭尾的空格和換行符
.xpath 方法返回列表
enumerate() 方法作用於列表,從 0 開始與列表中的元素一一對應
i 即爲一一對應的數字,j 即爲對應的元素(字符串)
而 roles 和 descritions 的區別就是但對應數字爲奇數時上面打印,反之下面打印

具體實例:

網頁源代碼:
導演: 弗蘭克·德拉邦特 Frank Darabont&nbsp;&nbsp;&nbsp;主演: 蒂姆·羅賓斯 Tim Robbins /...<br/>
1994&nbsp;/&nbsp;美國&nbsp;/&nbsp;犯罪 劇情

打印:
role:導演: 弗蘭克·德拉邦特 Frank Darabont   主演: 蒂姆·羅賓斯 Tim Robbins /...
descrition:1994 / 美國 / 犯罪 劇情

# 說明 <br/>是一個分隔符號,用於列表分隔元素

三、跳轉頁面

以往是根據 URL 的規律來進行頁數的爬取,但我們這次的有所不同,來看源代碼

def parse4link(html,base_url):
    link = None
    html_elem = etree.HTML(html)
    url = html_elem.xpath('//div[@id="paginator"]/a[@class="next"]/@href')
    if url:
        link = base_url + url[0]  # 加上匹配到的地址
    return link

# 我們可以發現評論首頁是單獨地址,從第二頁開始才變得有規律(首頁地址 + 匹配地址)

參考博客:
爬蟲系列(七) requests的基本使用
爬蟲系列(九) xpath的基本使用
爬蟲系列(十) 用requests和xpath爬取豆瓣電影
爬蟲系列(十一) 用requests和xpath爬取豆瓣電影評論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章