深度解析爬蟲(python+requests桨螺、python+selenium、golang)

什么是爬蟲酿秸?

爬蟲實(shí)際上就是采集網(wǎng)絡(luò)上數(shù)據(jù)的一段程序灭翔。
簡單來說,爬蟲程序通過請求url地址辣苏,然后根據(jù)響應(yīng)的內(nèi)容進(jìn)行數(shù)據(jù)采集缠局。如果響應(yīng)內(nèi)容是html,分析dom結(jié)構(gòu)考润,進(jìn)行dom解析或者正則匹配狭园,如果響應(yīng)內(nèi)容是xml/json數(shù)據(jù),轉(zhuǎn)換數(shù)據(jù)對象糊治,然后對數(shù)據(jù)進(jìn)行解析唱矛。

采集數(shù)據(jù)的用途

采集數(shù)據(jù)就是將別人的資源采集下來,然后加以利用井辜,變?yōu)樽约旱馁Y源绎谦。我們可以從某些網(wǎng)站獲取需要的圖片、數(shù)據(jù)粥脚、文章放到自己的網(wǎng)站中窃肠,也可將網(wǎng)站中的熱點(diǎn)采集下來,進(jìn)行數(shù)據(jù)分析刷允。

Beautiful Soup 是一個常用的網(wǎng)頁解析器冤留,可以從HTML或XML文件中提取數(shù)據(jù)的Python庫。它能夠通過你喜歡的轉(zhuǎn)換器實(shí)現(xiàn)慣用的文檔導(dǎo)航,查找,修改文檔的方式树灶。Beautiful Soup會幫你節(jié)省數(shù)小時甚至數(shù)天的工作時間纤怒。

Beautiful Soup文檔鏈接 https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/#

爬蟲注意事項

在很多網(wǎng)站中,通常會存在反爬操作天通,來阻止網(wǎng)站數(shù)據(jù)被爬取泊窘。如果長時間頻繁的使用爬蟲爬取某些網(wǎng)站數(shù)據(jù),容易造成IP被封情況。因此烘豹,為降低IP被封瓜贾,而導(dǎo)致爬取數(shù)據(jù)失敗的幾率,可使用隨機(jī)user-agent携悯,以及ip代理阐虚,和注意每次爬蟲的時間間隔。

一蚌卤、requests

requests是python實(shí)現(xiàn)爬蟲功能中簡單易用的HTTP庫实束,也是爬蟲過程中常用的一種工具庫,操作簡單逊彭,通過pip install requests命令進(jìn)行安裝咸灿,即可使用。但同樣也存在某些不足之處侮叮,比如在爬取某些js動態(tài)加載的網(wǎng)頁數(shù)據(jù)時避矢,就容易爬取不到相關(guān)數(shù)據(jù)。此時囊榜,可以嘗試使用selenium進(jìn)行數(shù)據(jù)爬取工作审胸。

# -*- coding: UTF-8 -*-
import requests,json
from bs4 import BeautifulSoup
# ip代理
proxies = {
  "http": "http://171.12.115.194:9999",
  "https": "http://171.12.115.194:9999",
}

class RequestsData():
    def __init__(self,url):
        self.url = url
        self.headers={
                'User-Agent':'Mozilla/5.0(Macintosh;Intel Mac OS X 10_11_4)AppleWebKit/537.36(KHTML,like Gecko)Chrome/52.0.2743.116 Safari/537.36'
            }
        resp = requests.get(self.url, headers=self.headers,proxies=proxies,verify=False)
        if resp.status_code != 200:
            return
        self.soup = BeautifulSoup(resp.text, 'html.parser', from_encoding='utf-8')
    def getData(self):
        div = self.soup.find("div",class_="market-bd market-bd-6 course-list course-card-list-multi-wrap js-course-list")
        ul = div.find("ul",class_="course-card-list")
        lis = ul.select("li")
        return lis
    def getSideData(self):
        contents = json.loads(self.soup.contents[0])
        data = contents["result"]["bottom_list"]
        return data

if __name__ == '__main__':
    obj = RequestsData(url='https://ke.qq.com/cgi-bin/course/courseListOtherModule?mt=0&bkn=1372242365&r=0.6038976779720526')
    data = obj.getSideData()
    # obj=RequestsData(url='https://ke.qq.com/course/list/golang?page=1')
    # data = obj.getData()
二. selenium

selenium 是一個用于Web應(yīng)用程序測試的工具,通過調(diào)用相應(yīng)瀏覽器的驅(qū)動程序卸勺,模擬用戶進(jìn)行操作砂沛。Selenium測試直接運(yùn)行在瀏覽器中,就像真正的用戶在操作一樣曙求。支持的瀏覽器包括IE(7, 8, 9, 10, 11)碍庵,Mozilla Firefox,Safari悟狱,Google Chrome静浴,Opera等。selenium 是一套完整的web應(yīng)用程序測試系統(tǒng)挤渐,包含了測試的錄制(selenium IDE),編寫及運(yùn)行(Selenium Remote Control)和測試的并行處理(Selenium Grid)苹享。
Selenium的核心Selenium Core基于JsUnit,完全由JavaScript編寫浴麻,因此可以用于任何支持JavaScript的瀏覽器上得问。
selenium可以模擬真實(shí)瀏覽器,自動化測試工具白胀,支持多種瀏覽器椭赋,爬蟲中主要用來解決JavaScript渲染問題。

Headless Chrome
Headless Chrome 是 Chrome 瀏覽器的無界面形態(tài)或杠,可以在不打開瀏覽器的前提下,使用所有Chrome支持的特性宣蔚,在命令行中運(yùn)行你的腳本向抢。以前在爬蟲要使用Phantomjs來實(shí)現(xiàn)這些功能认境,但Phantomjs已經(jīng)暫停開發(fā),現(xiàn)在可以使用Headless Chrome來代替挟鸠。

同樣叉信,selenium作為爬蟲的工具庫之一,雖然能夠模擬用戶打開瀏覽器進(jìn)行訪問艘希,但也同樣存在不足的地方硼身,比如爬蟲數(shù)據(jù)的時間比其他工具庫要長,并且使用也比較繁瑣覆享,需要下載相應(yīng)瀏覽器版本的驅(qū)動佳遂,否則,你可能會看到瀏覽器剛開啟撒顿,就關(guān)閉的場景丑罪。

selenium中文文檔鏈接:https://selenium-python-zh.readthedocs.io/en/latest/
查找chrome瀏覽器對應(yīng)版本的chromedriver,下載chromedriver 鏈接http://npm.taobao.org/mirrors/chromedriver/凤壁,查看chrome瀏覽器版本相關(guān)信息吩屹,在地址欄輸入chrome://version/即可

# -*- coding: UTF-8 -*-

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep
import random

class SeleniumData(object):
    def __init__(self,url='http://www.baidu.com/'):
        # 設(shè)置chrome為headless模式
        # chrome_options = webdriver.ChromeOptions()
        # chrome_options.add_argument('--headless')
        # self.browser = webdriver.Chrome(chrome_options=chrome_options)

        # 默認(rèn)可視化界面模式
        self.browser = webdriver.Chrome()
        self.url = url


    def getTengXunKeTang(self):
        self.browser.get(self.url)
        self.browser.find_element_by_id("js_keyword").send_keys("golang")
        sleep(1)
        self.browser.find_element_by_id("js_search").click()
        sleep(1)
        data = []
        courseCardList = self.browser.find_element_by_class_name("course-card-list")
        lis = courseCardList.find_elements_by_class_name("js-course-card-item")
        for li in lis:
            temp = {}
            a = li.find_element_by_tag_name("a")
            temp["url"] = a.get_attribute("href")
            title = li.find_element_by_class_name("item-tt").text
            temp["title"] = title
            temp["img"] = a.find_element_by_tag_name("img").get_attribute("src")
            data.append(temp)
            print temp
            sleep(1)
        print 1111111,len(data)

    def openBaiduLoginFrame(self):
        # 百度登錄爬蟲處理
        self.browser.get(self.url)
        self.browser.find_element_by_id("u1").find_element(By.NAME,"tj_login").click()
        sleep(1)
        self.browser.find_element(By.ID,"TANGRAM__PSP_11__footerULoginBtn").click()
        self.browser.find_element_by_id("TANGRAM__PSP_11__userName").send_keys("1805656666")
        sleep(1)
        self.browser.find_element_by_id("TANGRAM__PSP_11__password").send_keys("**********")
        sleep(1)
        self.browser.find_element_by_id("TANGRAM__PSP_11__submit").click()
        sleep(1)
        a = []
        i = 1
        while True:
            try:
                i = i + 1
                if i == 1000:
                    break
                b = random.choice([45,127,90,180,360])
                self.browser.execute_script("document.getElementByClassName('vcode-spin-button').style.transform=translateX("+str(b)+"px);")
                sleep(0.5)
            except Exception,e:
                continue
            a = a.append(b)
        print a

    def browserClose(self):
        self.browser.quit()

if __name__ == '__main__':
    obj = SeleniumData(url="https://ke.qq.com/")
    obj.getTengXunKeTang()
    obj.browserClose()
    # sleep(3)
    # baiduObj = SeleniumData()
    # baiduObj.openBaiduLoginFrame()
    # baiduObj.browserClose()

三、golang

通常爬蟲拧抖,我們首先想到的可能是用python進(jìn)行數(shù)據(jù)爬取煤搜,其實(shí)爬蟲并不局限于開發(fā)語言,golang也同樣可以爬取數(shù)據(jù)唧席,原理和以上所述相同宅楞,都是獲取url地址的響應(yīng)內(nèi)容,進(jìn)行解析袱吆。
同樣厌衙,前端js也可以做爬蟲程序,去采集相關(guān)數(shù)據(jù)绞绒,如使用axios爬取數(shù)據(jù)婶希,此處不做示例分析。
以下為通過golang做爬蟲程序的示例蓬衡,僅供參考喻杈。

package main

import (
    "time"
    "math/rand"
    "strings"
    "net/http"
    "fmt"
    "github.com/go-xweb/log"
    "io/ioutil"
    "encoding/xml"
    "regexp"
)

var userAgent = [...]string{"Mozilla/5.0 (compatible, MSIE 10.0, Windows NT, DigExt)",
    "Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, 360SE)",
    "Mozilla/4.0 (compatible, MSIE 8.0, Windows NT 6.0, Trident/4.0)",
    "Mozilla/5.0 (compatible, MSIE 9.0, Windows NT 6.1, Trident/5.0,",
    "Opera/9.80 (Windows NT 6.1, U, en) Presto/2.8.131 Version/11.11",
    "Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, TencentTraveler 4.0)",
    "Mozilla/5.0 (Windows, U, Windows NT 6.1, en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
    "Mozilla/5.0 (Macintosh, Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
    "Mozilla/5.0 (Macintosh, U, Intel Mac OS X 10_6_8, en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
    "Mozilla/5.0 (Linux, U, Android 3.0, en-us, Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
    "Mozilla/5.0 (iPad, U, CPU OS 4_3_3 like Mac OS X, en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
    "Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, Trident/4.0, SE 2.X MetaSr 1.0, SE 2.X MetaSr 1.0, .NET CLR 2.0.50727, SE 2.X MetaSr 1.0)",
    "Mozilla/5.0 (iPhone, U, CPU iPhone OS 4_3_3 like Mac OS X, en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
    "MQQBrowser/26 Mozilla/5.0 (Linux, U, Android 2.3.7, zh-cn, MB200 Build/GRJ22, CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"}

var urlChannel = make(chan string, 200) //chan中存入string類型的href屬性,緩沖200
var atagRegExp = regexp.MustCompile(`<a[^>]+[(href)|(HREF)]\s*\t*\n*=\s*\t*\n*[(".+")|('.+')][^>]*>[^<]*</a>`) //以Must前綴的方法或函數(shù)都是必須保證一定能執(zhí)行成功的,否則將引發(fā)一次panic
var r = rand.New(rand.NewSource(time.Now().UnixNano()))
func GetRandomUserAgent() string {
    return userAgent[r.Intn(len(userAgent))]
}

func Spider(url string){
    defer func() {
        if r := recover(); r != nil {
            log.Println("[E]", r)
        }
    }()
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("User-Agent", GetRandomUserAgent())
    client := http.DefaultClient
    res, e := client.Do(req)
    if e != nil {
        fmt.Errorf("Get請求%s返回錯誤:%s", url, e)
        return
    }

    if res.StatusCode == 200 {
        body := res.Body
        defer body.Close()
        bodyByte, _ := ioutil.ReadAll(body)
        resStr := string(bodyByte)
        atag := atagRegExp.FindAllString(resStr, -1)
        for _, a := range atag {
            href,_ := GetHref(a)
            if strings.Contains(href, "article/details/") {
                fmt.Println("☆", href)
            }else {
                fmt.Println("□", href)
            }
            urlChannel <- href
        }
    }
}

func GetHref(atag string) (href,content string) {
    inputReader := strings.NewReader(atag)
    decoder := xml.NewDecoder(inputReader)
    for t, err := decoder.Token(); err == nil; t, err = decoder.Token() {
        switch token := t.(type) {
        // 處理元素開始(標(biāo)簽)
        case xml.StartElement:
            for _, attr := range token.Attr {
                attrName := attr.Name.Local
                attrValue := attr.Value
                if(strings.EqualFold(attrName,"href") || strings.EqualFold(attrName,"HREF")){
                    href = attrValue
                }
            }
            // 處理元素結(jié)束(標(biāo)簽)
        case xml.EndElement:
            // 處理字符數(shù)據(jù)(這里就是元素的文本)
        case xml.CharData:
            content = string([]byte(token))
        default:
            href = ""
            content = ""
        }
    }
    return href, content
}

func main(){
    Spider("https://blog.csdn.net/")
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末星持,一起剝皮案震驚了整個濱河市矾飞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌羹呵,老刑警劉巖壁晒,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓷们,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)谬晕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門碘裕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人攒钳,你說我怎么就攤上這事帮孔。” “怎么了不撑?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵文兢,是天一觀的道長。 經(jīng)常有香客問我焕檬,道長姆坚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任揩页,我火速辦了婚禮旷偿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爆侣。我一直安慰自己萍程,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布兔仰。 她就那樣靜靜地躺著茫负,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乎赴。 梳的紋絲不亂的頭發(fā)上忍法,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音榕吼,去河邊找鬼饿序。 笑死,一個胖子當(dāng)著我的面吹牛羹蚣,可吹牛的內(nèi)容都是我干的原探。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼顽素,長吁一口氣:“原來是場噩夢啊……” “哼咽弦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胁出,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤型型,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后全蝶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闹蒜,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寺枉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嫂用。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片型凳。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡丈冬,死狀恐怖嘱函,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情埂蕊,我是刑警寧澤往弓,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蓄氧,受9級特大地震影響函似,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喉童,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一撇寞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堂氯,春花似錦蔑担、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晶框,卻和暖如春排抬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背授段。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工蹲蒲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人侵贵。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓届搁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親模燥。 傳聞我的和親對象是個殘疾皇子咖祭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355