爬蟲:使用selenium爬取豆瓣電影搜索結(jié)果混蔼,并存入MongoDB數(shù)據(jù)庫中

GitHub地址:https://github.com/al2ln44edr/spider_douban_selenium_mongodb

1榨了、前言

豆瓣電影(https://movie.douban.com/)眶蕉,是豆瓣網(wǎng)站的重要板塊之一勋乾,存有大量電影信息簡介执泰。

本文所記述的內(nèi)容是枕磁,使用selenium爬取豆瓣電影搜索結(jié)果,并存入MongoDB數(shù)據(jù)庫中术吝。

1.1.爬取對(duì)象

  • 電影名计济;

  • 演員名錄;

  • 詳情頁鏈接顿苇;

  • 電影時(shí)長峭咒;

  • 評(píng)分;

1.2.使用工具

  • selenium纪岁;

  • MongoDB凑队;


2、編碼過程

2.1.目標(biāo)網(wǎng)頁分析

第一步幔翰,打開豆瓣電影網(wǎng)站漩氨,打開開發(fā)者模式,查找并獲得【搜索框】遗增、【搜索圖標(biāo)】按鈕的element元素位置叫惊;

第二步,在搜索框中輸入【科幻】字樣做修,得到搜索結(jié)果頁霍狰,并獲得URL;

第三步饰及,在開發(fā)者模式中蔗坯,找到網(wǎng)頁底部“當(dāng)前頁”、【下一頁】的element元素位置燎含;

注意:

- 這里本人為了圖省事宾濒,直接采用點(diǎn)擊網(wǎng)頁【下一頁】的方法、跳轉(zhuǎn)到下一頁屏箍。

- 在多線程或者并發(fā)環(huán)境下绘梦,需要小伙伴使用其他方法構(gòu)造URL,比如變換頁碼赴魁、查看URL變化規(guī)律卸奉,使用“首頁URL+頁碼對(duì)應(yīng)的值”構(gòu)造URL。同時(shí)颖御,還要注意頁碼的上限值是多少择卦,在構(gòu)造URL時(shí)設(shè)置URL數(shù)量的上限。

2.2.定義【搜索】模塊


from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from lxml import etree

browser = webdriver.Chrome()
# 設(shè)置等待時(shí)間為10s郎嫁,超時(shí)則會(huì)報(bào)錯(cuò)
wait    = WebDriverWait(browser,10)
browser.get('https://movie.douban.com/')
# 可以在terminal命令行輸入搜索內(nèi)容
word = input('請(qǐng)輸入您要搜索的內(nèi)容>>> ')
def search():
    # 等待輸入框加載完畢
    input = wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR,'#inp-query'))
    )
    # 等待【搜索】按鈕標(biāo)簽加載完畢
    submit = wait.until(
    EC.element_to_be_clickable((By.CSS_SELECTOR,'#db-nav-movie > div.nav-wrap > div >     div.nav-search > form > fieldset > div.inp-btn > input[type="submit"]'))
    )
    print('輸入搜索的內(nèi)容【{}】'.format(word))
    # 輸入搜索內(nèi)容
    input.send_keys('{}'.format(word))
    # 點(diǎn)擊確認(rèn)
    submit.click()
    # 等待高亮頁碼加載完畢秉继,表示該網(wǎng)頁加載完
    active = wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR,'a.num.activate.thispage'))
    )
    print('加載第【{}】頁成功'.format(active.text))
    print(browser.page_source)

# 定義主程序

def main():
    search()

if __name__ == '__main__':

    main()

運(yùn)行,得到下圖所示內(nèi)容泽铛,代碼OK尚辑。

image

2.3.定義翻頁模塊

from ...
import time

... 

def next_page():
    # 等待【下一頁】按鈕標(biāo)簽加載完畢,這個(gè)主要是確保下一頁按加載完畢確實(shí)可用盔腔,目的是提高爬蟲健壯性杠茬,不喜歡的話可以使用next_page_submit = browser.find_element_by_css_selector('a.next')代替)

    next_page_submit = wait.until(
    EC.element_to_be_clickable((By.CSS_SELECTOR,'a.next'))
    )
    # 點(diǎn)擊【下一頁】
    next_page_submit.click()
    # 等待高亮當(dāng)前頁標(biāo)簽加載完畢
    wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR,'a.num.activate.thispage'))
    )
    print('成功加載該頁數(shù)據(jù)!')
    # 打印分頁符弛随,主要目的是為了區(qū)分不同頁的返回結(jié)果瓢喉,不喜歡可刪除
    print('--------------加載完成,并打印成功舀透,開始加載下一頁------------')
    # 設(shè)置睡眠時(shí)間為3s
    time.sleep(3)
    # 遞歸調(diào)用栓票,循環(huán)獲取下一頁
    next_page()

# 定義主程序
def main():
    ...
    # 主程序中添加next_page()模塊
    next_page()

if __name__ == '__main__':
    main()

運(yùn)行,得到下圖內(nèi)容愕够,代碼OK走贪!

image

2.4.定義網(wǎng)頁解析模塊,獲取網(wǎng)頁內(nèi)容

from ...

import re

... 

def search():
    ...

    # 注意: 由于翻頁使用的是遞歸調(diào)用惑芭,需要在搜索完成得到第一頁時(shí)坠狡,調(diào)用get_movies()模塊,以獲取第一頁解析數(shù)據(jù)*
    get_movies()

def next_page():
    ...
    # 注意:由于翻頁使用的是遞歸調(diào)用遂跟,需要在翻頁動(dòng)作完成后逃沿,調(diào)用get_movies()模塊,以獲取第二頁及之后網(wǎng)頁的解析數(shù)據(jù)*
    get_movies()

def get_movies():
    print('正在解析網(wǎng)頁...')
    page = browser.page_source
    selector = etree.HTML(page)
    print('開始打印輸出電影信息...')
    # 獲取主div模塊
    items = selector.xpath('//*[@id="root"]/div/div[2]/div[1]/div[1]')
    for item in items:
        # 獲取電影姓名
        names = item.xpath('div/div/div/div[1]/a/text()')
        # 獲取電影詳情頁URL
        urls = item.xpath('div/div/div/div[1]/a/@href')
        # 獲取電影評(píng)分
        ratings = item.xpath('div/div/div/div[2]/span[2]/text()')
        # 注意:item.xpath()返回的是列表幻锁,需要使用str將其字符化*
        durations = re.findall(r'\d\d+',str(item.xpath('div/div/div/div[3]/text()')))
        actors = item.xpath('div/div/div/div[4]/text()')
        注意:由于xpath返回的是列表格式凯亮,而我們需要將列表中的元素一一對(duì)應(yīng)存放至字典中,這就需要使用zip()函數(shù)越败,將內(nèi)容存放至空字典中*
        for name,url,rating,duration,actor in zip(names,urls,ratings,durations,actors):
            # 創(chuàng)建單條電影信息為一個(gè)字典触幼,并以鍵值對(duì)形式賦值
            movie_info = {}
            movie_info['name'] = name
            movie_info['url'] = url
            # 如果電影未上映,網(wǎng)頁中評(píng)分所在element無數(shù)值究飞,則需要賦值為None置谦,否則會(huì)報(bào)錯(cuò)
            if rating == '(尚未上映)' or '(暫無評(píng)分)':
                movie_info['rating'] = None
            else:
                movie_info['rating'] = float(rating)
            movie_info['duration'] = int(duration)
            movie_info['actors'] = actor
            print(movie_info)

def main():
    ...

if __name__ == '__main__':
    main()

運(yùn)行,得到如圖結(jié)果亿傅,代碼OK媒峡!

image

2.4.完成MongoDB數(shù)據(jù)庫操作

首先,在MongoDB的client中創(chuàng)建名為【doubandianying】的DB葵擎;

其次谅阿,在【doubandianying】中創(chuàng)建名為【movie_info】的collection;

再次,定義存儲(chǔ)到MongoDB模塊签餐;

...

# 由于使用遞歸翻頁寓涨,所以在get_movies()模塊中添加save_to_mongo()模塊功能
def get_movies():
    ...
    save_to_mongo(movie_info)

# 定義存儲(chǔ)模塊,并添加result作為參數(shù)
def save_to_mongo(result):
    if db[MONGO_TABLE].insert_one(result):
    print('成功存儲(chǔ)到MONGODB')

...

這步比較簡單氯檐,不再運(yùn)行戒良。

2.5.爬蟲功能優(yōu)化

首先,為提高爬蟲健壯性冠摄,添加 try ... except ... 相關(guān)內(nèi)容糯崎;

其次,對(duì)于sear()和next_page()模塊河泳,如遇加載超時(shí)情況沃呢,則遞歸調(diào)用本模塊,以提高健壯性拆挥;

再次薄霜,增加爬蟲時(shí)間計(jì)算功能。


3.完整代碼

# -*- coding:utf-8 -*-

from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re
from selenium.common.exceptions import TimeoutException
from config import *
from lxml import etree
import pymongo
import datetime

client = pymongo.MongoClient(MONGO_URL)
db    = client[MONGO_DB]
MONGO_URL = 'localhost'
MONGO_DB    = 'doubandianying'
MONGO_TABLE = 'movie_info'

browser = webdriver.Chrome()
wait    = WebDriverWait(browser,10)
browser.get('https://movie.douban.com/')
word = input('請(qǐng)輸入您要搜索的內(nèi)容>>> ')

def search():
    try:
        input = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR,'#inp-query'))
            )
        submit = wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR,'#db-nav-movie > div.nav-wrap > div > div.nav-search > form > fieldset > div.inp-btn > input[type="submit"]'))
            )
        print('輸入搜索的內(nèi)容【{}】'.format(word))
        input.send_keys('{}'.format(word))
        submit.click()
        print('正在加載')
        active = wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR,'a.num.activate.thispage'))
            )
        print('加載第【{}】頁成功'.format(active.text))
        get_movies()

except TimeoutException:
    print('等待超時(shí)竿刁,重新搜索...')
    return search()

def next_page():
    try:
        next_page_submit = wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR,'a.next'))
            )
        next_page_submit.click()
        wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR,'a.num.activate.thispage'))
            )
        print('成功加載該頁數(shù)據(jù)...')
        get_movies()
        print('--------------加載完成黄锤,并打印成功,開始加載下一頁------------')
        time.sleep(3)
        next_page()
    except TimeoutException:
        print('加載超時(shí)食拜,重新加載...')
        return next_page()

def get_movies():
    try:
        print('正在解析...')
        page = browser.page_source
        selector = etree.HTML(page)
        print('開始打印輸出電影信息...')
        items = selector.xpath('//*[@id="root"]/div/div[2]/div[1]/div[1]')
        for item in items:
            names = item.xpath('div/div/div/div[1]/a/text()')
            urls = item.xpath('div/div/div/div[1]/a/@href')
            ratings = item.xpath('div/div/div/div[2]/span[2]/text()')
            durations = re.findall(r'\d\d+',str(item.xpath('div/div/div/div[3]/text()')))
            actors = item.xpath('div/div/div/div[4]/text()')
            for name,url,rating,duration,actor in zip(names,urls,ratings,durations,actors):
                movie_info = {}
                movie_info['name'] = name
                movie_info['url'] = url
                if rating == '(尚未上映)' or '(暫無評(píng)分)':
                    movie_info['rating'] = None
                else:
                    movie_info['rating'] = float(rating)
            movie_info['duration'] = int(duration)
            movie_info['actors'] = actor
            print(movie_info)
            save_to_mongo(movie_info)
    
    except Exception as e:
        print(e)
        time.sleep(3)
        return get_movies()

def save_to_mongo(result):
    try:
        if db[MONGO_TABLE].insert_one(result):
            print('成功存儲(chǔ)到MONGODB')
    except Exception as e:
        raise e

def main():
    start_time = datetime.datetime.now()
    try:
        search()
        next_page()
    except Exception as e:
        raise e
    finally:
        browser.close()
    end_time = datetime.datetime.now()
    print('開始時(shí)間:',start_time)
    print('結(jié)束時(shí)間:',end_time)

if __name__ == '__main__':
    main()

4.后記

該爬蟲是入門級(jí)爬蟲鸵熟,爬取的是搜索框返回的一級(jí)頁面的信息;

該爬蟲屬單進(jìn)程\單線程爬蟲负甸,消耗時(shí)間較長流强,效率不高;

GitHub地址:https://github.com/al2ln44edr/spider_douban_selenium_mongodb

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呻待,一起剝皮案震驚了整個(gè)濱河市打月,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蚕捉,老刑警劉巖奏篙,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異迫淹,居然都是意外死亡秘通,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門敛熬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肺稀,“玉大人,你說我怎么就攤上這事应民』霸” “怎么了夕吻?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長繁仁。 經(jīng)常有香客問我涉馅,道長,這世上最難降的妖魔是什么改备? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任控漠,我火速辦了婚禮,結(jié)果婚禮上悬钳,老公的妹妹穿的比我還像新娘。我一直安慰自己偶翅,他們只是感情好默勾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著聚谁,像睡著了一般母剥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上形导,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天环疼,我揣著相機(jī)與錄音,去河邊找鬼朵耕。 笑死炫隶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阎曹。 我是一名探鬼主播伪阶,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼处嫌!你這毒婦竟也來了栅贴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤熏迹,失蹤者是張志新(化名)和其女友劉穎檐薯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體注暗,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坛缕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了友存。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祷膳。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖屡立,靈堂內(nèi)的尸體忽然破棺而出直晨,到底是詐尸還是另有隱情搀军,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布勇皇,位于F島的核電站罩句,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏敛摘。R本人自食惡果不足惜门烂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兄淫。 院中可真熱鬧屯远,春花似錦、人聲如沸捕虽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泄私。三九已至房揭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晌端,已是汗流浹背捅暴。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咧纠,地道東北人蓬痒。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像惧盹,于是被迫代替她去往敵國和親乳幸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容