1. 程式人生 > >python之Selenium+pyquery爬取有大量反爬蟲的天眼查

python之Selenium+pyquery爬取有大量反爬蟲的天眼查

天眼查:一個還有大量公司的資訊的網站。
所以反爬程度是相當高的,首先直接用requests.get(url)來獲取頁面原始碼,你會發現,明明顯示在頁面上的公司的一些資料都不在,他是利用其它的js的方法表達出來的,因為這個網站有專門的反爬蟲人員,可以在一些招聘網上看到工資還可以15k-30k
所以說用這些方法根本就不爬到什麼
那麼只有使出我們的殺手鐗,selenium,他的好處在於可以模擬瀏覽器操作,非常方便獲取屬性也很方便
首先這個網站不登入的話,你會發現,怎麼原始碼上面的是和頁面表示出來的不一樣啊,明明是****公司**,在原始碼上卻是 @#¥····,連公司的基本資訊,比如郵箱什麼的,都是*****

[email protected]什麼的,不能看到全部的資訊,這是因為不登入的話,天眼查對於大部分的公司的資訊,進行了一些保護,登入了才可以頁面的原始碼才會正確。
所以首先我們註冊註冊一個天眼查的賬號:這裡寫圖片描述

接下來我們知道selenium驅動的瀏覽器是一個非常純淨的,沒有任何cookies,及一些資訊的儲存,目前來說是這樣,所以每一次訪問這個網站,都是全新的,沒有任何的登入資訊在上面,所以我們怎麼實現selenium模擬登入呢,這就要用到phantomjs,因為這個網站上面的登入註冊時用js程式碼生成的,原始碼上是沒有這些東西的,如果我們用phantomjs的話,可以直接解析出來所有js包含的html程式碼


下面一步一步來解析程式碼:
這是拼接完整的程式碼,keyword就是關鍵字,等會在main函式裡自行輸入,在search函式里加入phantomjs解析,這個要先安裝好phantomjs。

browser = webdriver.Chrome()
browser.maximize_window()#將瀏覽器最大化
wait = WebDriverWait(browser,10)

def search(keyword):
    url_keyword = urllib.parse.quote(keyword)  # 中文轉碼為url格式
    url = 'http://www.tianyancha.com/search/'
+ url_keyword + '?checkFrom=searchBox' driver = webdriver.PhantomJS( executable_path='D:\\Program Files\\phantomjs-2.1.1-windows\\bin\\phantomjs') # phantomjs的絕對路徑 time.sleep(2) driver.get(url) browser.get(url) return url def main(): keyword = '阿里巴巴' url = search(keyword) if __name__ == '__main__': main()

當我們執行程式碼後發現:怎麼回事啊,為什麼和我不加phantomjs程式碼不一樣啊,現在直接出現一個登陸的介面,需要登陸之後才能進入,這就是為什麼要註冊一個賬號的原因之一
這裡寫圖片描述
現在我們就需要利用selenuim模擬登入了,首先要做到的就是獲取輸入框,在剛剛這個介面按F12審查元素,再點選選擇元素的小箭頭,之後點選輸入手機號碼那裡,就可以在下面看到輸入框在原始碼中的位置,找到那個位置,右鍵,copy,selector,就可以得到他的這個元素的選擇器,因為我們用的是selenuim中一個查詢元素為位置的元素,具體看我的程式碼,之後用send_keys()輸入你的賬戶密碼,然後同樣的找到登入的那個位置的css_selector,再點選就進入了。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException

def login():
    try:
        userInput = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#web-content > div > div > div > div.position-rel.container.company_container > div > div.in-block.vertical-top.float-right.right_content.mt50.mr5.mb5 > div.module.module1.module2.loginmodule.collapse.in > div.modulein.modulein1.mobile_box.pl30.pr30.f14.collapse.in > div.pb30.position-rel > input')))
        passwordInput = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#web-content > div > div > div > div.position-rel.container.company_container > div > div.in-block.vertical-top.float-right.right_content.mt50.mr5.mb5 > div.module.module1.module2.loginmodule.collapse.in > div.modulein.modulein1.mobile_box.pl30.pr30.f14.collapse.in > div.pb40.position-rel > input')))
        userInput.send_keys('*************')#賬號
        passwordInput.send_keys('**********')#密碼
        changeLoginWay = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#web-content > div > div > div > div.position-rel.container.company_container > div > div.in-block.vertical-top.float-right.right_content.mt50.mr5.mb5 > div.module.module1.module2.loginmodule.collapse.in > div.modulein.modulein1.mobile_box.pl30.pr30.f14.collapse.in > div.c-white.b-c9.pt8.f18.text-center.login_btn')))
        changeLoginWay.click()
    except TimeoutException:
        login()

我們可以看到每個公司的class為search-result-single,
這裡寫圖片描述
直接看程式碼:
這裡就要用到pyquery了,用它來解析公司的每一個資訊:具體pyquery使用方法就不詳談了,具體可以看我上一篇文章。

def get_company_info():
    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.search-block .result-list .search-result-single')))
    html = browser.page_source#獲取此時頁面原始碼
    doc = pq(html)
    items = doc('.search-block .result-list .search-result-single').items()#解析到每一個公司的位置
    for item  in items:
        company = {
            'urlInfo':item.find('.name ').attr('href'),#公司連結
            'name' : item.find('.name').text(),#名字
            'LegalRepresentative':item.find('.info .title a').text(),#法人
            'registerMoney':item.find('.info .title span').eq(0).text(),#註冊資金
            'registerTime':item.find('.info .title span').eq(1).text(),#註冊時間
            'tel':item.find('.contact .link-hover-click').eq(0).text(),#電話
            'email':item.find('.contact .link-hover-click').eq(1).text(),#郵箱
            'LegalPersonInfo':item.find('.content .match span').eq(1).text()#商標資訊
        }

現在到了最關鍵的一步,就是翻頁,因為有很多頁,目前不是vip只有5頁可以查詢
下面是selenuim關於翻頁的操作:這裡面有很多坑,當時也折騰我了好久,
下面我來一一分析,當他在第一頁的時候,下面有個企業認證的廣告,剛好把翻頁的按鈕擋住了,當時這個bug我調了好久才找到,我們就用剛剛介紹的元素選擇器把它關掉就行了,把它關掉才可以點選下一頁那個按鈕,
第二個坑出現了,居然第一頁的下一步的按鈕和第二頁第三頁的selector不一樣,這裡也調了很久,程式碼裡可以看到我寫了判斷,最後再**執行get_company_info()**獲取資訊就行了。main裡面的那個迴圈就是用來翻頁的,從1到5頁。
move_to_bottom是用來翻到網頁最下面的。

def move_to_bottom():
    js = "var q=document.documentElement.scrollTop=100000"
    browser.execute_script(js)

def next_page(page_number):
    print('------------------------------------正在爬取',page_number,'頁-----------------------------------------------------------------')
    try:
        if page_number == 2:
            closeAdvertisement = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#tyc_banner_close')))
            closeAdvertisement.click()
        move_to_bottom()
        if page_number == 2:
            nextPageButton = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#web-content > div > div.container-left > div.search-block > div.result-footer > div:nth-child(1) > ul > li:nth-child(12) > a')))
            nextPageButton.click()
        if page_number > 2:
            nextPageButton = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#web-content > div > div.container-left > div.search-block > div.result-footer > div:nth-child(1) > ul > li:nth-child(13) > a')))
            nextPageButton.click()
        get_company_info()
        time.sleep(3)
    except TimeoutException:
        next_page(page_number)
def main():
    keyword = '阿里巴巴'
    url = search(keyword)
    login()
    for page in range(2,6):
        next_page(page)
    browser.close()

最後儲存到mongodb:
在get_company_info()方法裡呼叫就行了。

import pymongo

mongoclient = pymongo.MongoClient("localhost",port=27017,connect = False)
db = mongoclient .AugEleventh
classname = db.alibaba
def save_to_mongo(result):
    if classname.insert(result):
        print('儲存到mongoDB成功',result)
    else:
        print('儲存到mongoDb失敗')

這裡告訴大家基本以及爬完了,你想爬什麼公司的在keyword中輸入就可以了,但是不是vip只有最多5頁的內容。這個不用vip獲取更多公司資訊就說了,畢竟人家要靠這個賺錢,就放過他吧(其實是我完全不知道!!)
下面貼出完整程式碼:

from selenium import webdriver
import urllib
from pyquery import PyQuery as pq
import json
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
import time
import pymongo

mongoclient = pymongo.MongoClient("localhost",port=27017,connect = False)
db = mongoclient .AugEleventh
classname = db.alibaba

browser = webdriver.Chrome()
browser.maximize_window()#將瀏覽器最大化
wait = WebDriverWait(browser,10)

def move_to_bottom():
    js = "var q=document.documentElement.scrollTop=100000"
    browser.execute_script(js)

def search(keyword):
    url_keyword = urllib.parse.quote(keyword)  # 中文轉碼為url格式
    url = 'http://www.tianyancha.com/search/' + url_keyword + '?checkFrom=searchBox'
    driver = webdriver.PhantomJS(
        executable_path='D:\\Program Files\\phantomjs-2.1.1-windows\\bin\\phantomjs')  # phantomjs的絕對路徑
    time.sleep(2)
    driver.get(url)
    browser.get(url)
    return url



def get_total_page(url):
    total = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#web-content > div > div.container-left > div.search-block > div.result-footer > div:nth-child(1) > ul > li:nth-child(11) > a')))
    get_company_info()
    return total.text

def login():
    try:
        userInput = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#web-content > div > div > div > div.position-rel.container.company_container > div > div.in-block.vertical-top.float-right.right_content.mt50.mr5.mb5 > div.module.module1.module2.loginmodule.collapse.in > div.modulein.modulein1.mobile_box.pl30.pr30.f14.collapse.in > div.pb30.position-rel > input')))
        passwordInput = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#web-content > div > div > div > div.position-rel.container.company_container > div > div.in-block.vertical-top.float-right.right_content.mt50.mr5.mb5 > div.module.module1.module2.loginmodule.collapse.in > div.modulein.modulein1.mobile_box.pl30.pr30.f14.collapse.in > div.pb40.position-rel > input')))
        userInput.send_keys('15995028879')
        passwordInput.send_keys('19981027lcy')
        changeLoginWay = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#web-content > div > div > div > div.position-rel.container.company_container > div > div.in-block.vertical-top.float-right.right_content.mt50.mr5.mb5 > div.module.module1.module2.loginmodule.collapse.in > div.modulein.modulein1.mobile_box.pl30.pr30.f14.collapse.in > div.c-white.b-c9.pt8.f18.text-center.login_btn')))
        changeLoginWay.click()
    except TimeoutException:
        login()

def get_company_info():
    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.search-block .result-list .search-result-single')))
    html = browser.page_source
    doc = pq(html)
    items = doc('.search-block .result-list .search-result-single').items()
    for item  in items:
        company = {
            'urlInfo':item.find('.name ').attr('href'),
            'name' : item.find('.name').text(),
            'LegalRepresentative':item.find('.info .title a').text(),
            'registerMoney':item.find('.info .title span').eq(0).text(),
            'registerTime':item.find('.info .title span').eq(1).text(),
            'tel':item.find('.contact .link-hover-click').eq(0).text(),
            'email':item.find('.contact .link-hover-click').eq(1).text(),
            'LegalPersonInfo':item.find('.content .match span').eq(1).text()
        }
        save_to_mongo(company)

def next_page(page_number):
    print('------------------------------------正在爬取',page_number,'頁-----------------------------------------------------------------')
    try:
        if page_number == 2:
            closeAdvertisement = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#tyc_banner_close')))
            closeAdvertisement.click()
        move_to_bottom()
        if page_number == 2:
            nextPageButton = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#web-content > div > div.container-left > div.search-block > div.result-footer > div:nth-child(1) > ul > li:nth-child(12) > a')))
            nextPageButton.click()
        if page_number > 2:
            nextPageButton = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#web-content > div > div.container-left > div.search-block > div.result-footer > div:nth-child(1) > ul > li:nth-child(13) > a')))
            nextPageButton.click()
        get_company_info()
        time.sleep(3)
    except TimeoutException:
        next_page(page_number)

def save_to_mongo(result):
    if classname.insert(result):
        print('儲存到mongoDB成功',result)
    else:
        print('儲存到mongoDb失敗')

def main():
    keyword = '阿里巴巴'
    url = search(keyword)
    login()
    total_pages = int(get_total_page(url)[-3:])#獲取下他的總頁數,但是沒用,因為不是vip只能爬5頁,本來我不是想吧下面那個6換成total_pages 的
    for page in range(2,6):
        next_page(page)
    browser.close()

if __name__ == '__main__':
    main()

目前的我的模擬登入不是最好的,還有種是直接利用登入後的cookies資訊,用selenuim登入後直接就是已經登陸的狀態,目前我還不怎麼清楚怎麼做,希望有大神在評論區說說。