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尚辑。
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走贪!
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媒峡!
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