都9102年了,你還只會用python爬蟲嗎?

前言

無聊的時候,咱們總會打開豆瓣電影榜,從上到下刷着電影,彷彿那些經典臺詞立刻就在咱們耳邊響起,如《肖申克的救贖》中:生活能夠歸結爲一種簡單的選擇:不是忙於生存,就是趕着去死;《霸王別姬》中:說的是一生!差一年,一個月,一天,一個時辰,都不算一生!可是,豆瓣電影那麼多,還有那麼多頁,一頁一頁的找豈不是太費時間了,今天我就來從前端的角度來爬取豆瓣電影榜top250的首頁。豆瓣top250javascript

正文

1. 獲取豆瓣首頁的html

因爲咱們須要向豆瓣的服務器發送一個請求來獲得豆瓣首頁的html代碼,請求與響應在網絡傳輸中須要必定的時間,然後續的操做須要在這個響應的基礎上進行,所以,咱們須要將主函數定義爲異步的。以上實現須要引入下面兩個包(注意:request 是對等依賴,須要與request-promise 分開安裝)html

npm i request
    npm i request-promise  
複製代碼
let request = require('request-promise')
    const main = async() => {
      let html = await request({
        url'https://movie.douban.com/top250'
      })
      console.log(html)
    }
    main();
複製代碼

2.對html結構分析

這樣,咱們就獲得了豆瓣首頁的html代碼,可是咱們須要的只是不多的一部分信息,因此咱們要對其中的信息進行提取,所以咱們引入一個npm包cheerio來提取html 特定選擇器中的文本內容前端

npm i cherrio  
複製代碼

而後在第二行引入java

let cheerio = require('cheerio');
複製代碼

接下來咱們要作的就是利用cheerio 將獲得的html文本載入內存構建DOM,並使用選擇器對目標標籤中的文本進行提取,所以咱們須要分析豆瓣首頁的html結構node

content

content中是咱們須要的內容,gird-16-8 用於設置主體內容的佈局,article是電影項的主體部分,咱們須要的內容就在其中,aside 置於網頁右側放置醒目信息,extra用於清除兩端浮動。如今讓咱們進入article中看看


咱們能夠看到是一個典型的head,body,foot結構
opt mod 內放置一個複選框,用於用戶選擇根據條件篩選
grid_view 提取內容的主體
paginator 頁尾的分頁導航 進入grid_view,咱們所須要的內容都放置在一個一個的列表中,列表中的每個item都是一個咱們須要提取的一部電影。

3.使用選擇器獲得須要的內容

根據上面咱們對結構的分析,咱們就可使用下列語句獲得咱們選擇器內的每個itempython

let movieNodes = $('#content .article .grid_view').find('.item');
複製代碼

而後咱們對保存了全部item的這樣一個數組遍歷,將其中每一項的關鍵值提取出來並保存,選擇器的構建方法與上述的item得到的方法一致npm

let movies = [];
for (let i = 0; i < movieNodes.length; i++) {
    let $ = cheerio.load(movieNodes[i]);
    let titles = $('.info .hd span');
    titles = ([]).map.call(titles, t => $(t).text());
    let bd = $('.info .bd');
    let info = bd.find('p').text();
    let score = bd.find('.star .rating_num').text();
    movies.push({
      'titles': titles,
      'info': info,
      'score': score
    })
  }
複製代碼

不一樣的是,因爲.hd 的選擇器下面有四個span,所以咱們獲得的是一個對象數組,咱們不能直接進行提取不然會出現對象循環引用的錯誤,咱們先使用map對空數組的每個元素進行映射,而後再經過改變this指針,使用選擇器對空數組中每個元素進行賦值。而後將提取出來的每個字段append到數組中。編程

4.文件保存

接下來,咱們在第三行加上json

let fs = require('fs');
複製代碼

在for循環的外面繼續補充api

fs.writeFile('./mainjs.json'JSON.stringify(movies), 'utf-8', (err) => {
    if (err)
      console.log('寫入失敗');
    else
      console.log('寫入成功');
  });
複製代碼

就能夠將咱們剛剛在存在數組中的數據以json格式寫入文件中啦,打開這個文件就會發現豆瓣top250的首頁內容就存到了這個文件中,咱們就能夠挑選本身喜歡的電影觀看了。 如今,讓咱們回過頭來看看豆瓣top250首頁的頁面結構

body豆瓣的首頁由100%的body佔據,body又分爲上中下三部分,每一個版塊又按照必定的功能再劃分爲相應的子模塊,直到劃分爲一個最小的模塊爲止,每個模塊佔據網頁的必定位置,實現某一個特定的功能,每一個模塊之間互不干擾。

總結

經過對豆瓣的爬取,咱們知道了,若是一個網站的結構帶有必定規律,並有良好的可讀性,會讓咱們的爬取更加簡單,相反,若是一個網站亂糟糟的,毫無章法可言,那麼這樣的網站簡直就是災難。所以,咱們在從此的編程中,應當多看看一些大型網站的html代碼,學習其中的結構設計和命名規範。而後咱們可使用BEM(Block Element Modifier)命名規範,對每個模塊使用合適的選擇器,這樣不只提升了代碼的可重用行,也利於網站的管理和維護。 暑假閒來無事看了點python視頻,雖然已經忘得都差很少了,可是用了node爬取了一次,又讓我又拾起的對python的熱情,因而一番折騰,查了相關文檔,終於給搗鼓出來了,畢竟,生命在於折騰嘛! 如下是python的爬蟲方式,基本思路與上述爬蟲方式相似,只是對數據處理的語法上有所差別:
須要安轉requests和beautifulsoup4模塊

pip install requests
pip install beautifulsoup4
複製代碼
import requests
from bs4 import BeautifulSoup
import json
def gettext(x): 
    return x.get_text()
#設置請求頭假裝
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'}
# 先使用requests發送網絡請求從而獲取網頁
html = requests.get('https://movie.douban.com/top250', headers=header)
# print(html.status_code)
# 傳入html構建DOM
soup=BeautifulSoup(html.content,'html.parser')
movieNodes = soup.find('ol', attrs={'class': 'grid_view'})
movies=[]
for movie in movieNodes.find_all('li'):
    title=movie.find('div',attrs={'class':'hd'})
    titles=title.find_all('span')
    titles1=map(gettext,titles)
    tit=[]
    for ti in titles1:
      tit.append(ti)
    info=movie.find('p').get_text()
    score=movie.find('span',attrs={'class':'rating_num'}).get_text()
    movies.append({
      "titles": tit,
      "info": info,
      "score": score
    })
f = open("mainpy.json", "w", encoding='utf-8')
json.dump(movies, f, ensure_ascii=False)
f.close()
複製代碼
  • 寫的時候遇到了如下這些小問題
  1. 爬取豆瓣首頁時,打印不出來html源碼,查了一下才想起來是豆瓣作了反爬措施,須要假裝一下請求頭信息。因此之後咱們在爬取網站的時候能夠先打印一下狀態碼status_code,若是顯示418就說明被屏蔽了,顯示200就說明請求成功。
  2. 使用find_all時,返回的是一個object數組,須要使用map方法提取出item 中每個span的內容,而後添加到數組中去。map方法的使用能夠查看相關的文檔,裏面有對每個參數具體說明。
  3. 寫文件的時候,報錯說不能寫入一個list,必須寫入一個字符串,而後我將這個數組轉成字符串寫入文件,結果裏面對轉義字符沒有進行轉義,多了'\xa0','\n' 等,查了一下發現這個玩意兒'\xa0'居然就是html中的'&nbsp',而後我就用replace把每個這個字符給刪掉,結果存是存進去了,文件全爆紅色,對比了mainjs.json才發現,保存的文件mainpy.json不是一個Json類型的數據,而後又是一頓度娘,發現有個專門寫json文件的api,json.dump(),而後我replace直接也不用寫了,不過須要引入json庫....

結束語

可能有時候寫程序就是這樣,出一個問題而後你就去解決這個問題,而後又出現另外的問題,你又得想怎麼去解決新出現的問題,而後一直一直....結果發現直接使用一個api就行了...可是寫程序注重的是過程,bug是咱們編程道路上的補品,咱們不能作一個apier,不是嗎?

相關文章
相關標籤/搜索