爬蟲(4)--- 一起來爬廣東高兄澹快遞信息


目錄
1.“看不見”的數(shù)據(jù)
2. 讓數(shù)據(jù)現(xiàn)身
  2.1 數(shù)據(jù)API分析
    2.1.1 數(shù)據(jù)文件在哪里肄梨?
    2.1.2 guid(數(shù)據(jù)全局唯一標識符)在哪里?
    2.2.3 如何獲取guid挠锥?
3. 爬蟲構(gòu)建
  3.1 搭建爬蟲環(huán)境
  3.2 瀏覽器偽裝
  3.3 構(gòu)建代理IP池
  3.4 廣東高校數(shù)據(jù)獲取
  3.5 數(shù)據(jù)獲取與解析
  3.6 數(shù)據(jù)存儲
  3.7 主函數(shù)
4. 結(jié)果與可視化
  4.1 結(jié)果表結(jié)構(gòu)
  4.2 數(shù)據(jù)可視化


1.“看不見”的數(shù)據(jù)

反爬機制層出不窮众羡,其中很重要的一種手法是對數(shù)據(jù)進行隱藏,即在源碼中看不到實際的數(shù)據(jù)蓖租,主要手段如下:

  • 將數(shù)據(jù)存儲于json/XHR等文件中粱侣,如拉勾網(wǎng)、京東商城價格信息等蓖宦;
  • 直接隱藏數(shù)據(jù)甜害,需要點擊才能查看完整數(shù)據(jù),如快遞100網(wǎng)等
  • ......

2.讓數(shù)據(jù)現(xiàn)身

  本章節(jié)以爬取快遞100網(wǎng)快遞員手機號碼球昨、公司與歸屬地等信息為例。

2.1 數(shù)據(jù)API分析

快遞100是一個集快遞單號查詢眨攘、快遞單號短信跟蹤主慰、快遞網(wǎng)點查詢、網(wǎng)上寄快遞等為一體的綜合性快遞物流服務網(wǎng)站鲫售。其上面的某些較為敏感信息共螺,如快遞員手機號碼的反扒機制是采用隱藏信息的方式,如搜索金蝶大廈附近的快遞員情竹,結(jié)果如下圖:

反扒機制

用戶只有通過按鈕“點擊查看完整號碼”才能看到完整號碼藐不,即使點擊查看完整號碼之后在源碼中仍不能看到號碼所在,很明顯其數(shù)據(jù)已經(jīng)被隱藏,我們可以猜測其數(shù)據(jù)可能被存儲于XHR或者js文件中雏蛮,接下來我們的目的便在研究數(shù)據(jù)到底通過哪種方式隱藏起來涎嚼?我們該怎么獲取挑秉?

2.1.1 數(shù)據(jù)文件在哪里法梯?

(1)啟動Chrome瀏覽器
打開快遞100搜索頁面,并按F12打開開發(fā)環(huán)境犀概;

(2)如下圖選定XHR

(3)點擊按鈕“點擊查看完整號碼”

我們可以看到其產(chǎn)生了一個XHR文件立哑,這個很可能便是數(shù)據(jù)所在。打開該文件姻灶,如下圖铛绰,這果然便是我們數(shù)據(jù)所在。


  可以看到數(shù)據(jù)的URL如下产喉,構(gòu)造該XHR的關鍵就在于后面的guid值(唯一標識符)捂掰。所以問題便轉(zhuǎn)換為如何找到每個搜索關鍵詞的guid?

https://www.kuaidi100.com/courier/searchapi.do?method=courierdetail&json={%22guid%22:%22399F251E9C6C464FCDC93A73C93BD391%22}

2.1.2 guid(數(shù)據(jù)全局唯一標識符)在哪里镊叁?

在尋找guid之前尘颓,我們先看一下該網(wǎng)站的用戶行為觸發(fā)操作,即當用戶點擊“查看完整號碼”時候網(wǎng)頁將產(chǎn)生什么晦譬?

(1)查看網(wǎng)頁源碼

我們點擊“查看完整號碼”之后疤苹,右擊查看源碼并在源碼中搜索該號碼,納尼敛腌,沒能找到號碼卧土,就連收件地址也找不到。很明顯像樊,網(wǎng)頁是將相關信息給隱藏起來了尤莺。

在此必須明白所謂源碼,就是網(wǎng)站服務器發(fā)送到瀏覽器的原封不動的代碼生棍,上面我們又知道每次點擊“查看完整號碼”的時候會產(chǎn)生一個XHR文件并且頁面的號碼也全部顯示出來颤霎,瀏覽器執(zhí)行js動態(tài)肯定會產(chǎn)生新的HTML代碼,我們在源碼中找不到的代碼涂滴,但通過審查元素便能看到最終的HTML代碼友酱。我們接下來我們通過審查元素進行分析。

(2)檢查元素

將鼠標移至“點擊查看完整號碼”上面柔纵,點擊右鍵檢查缔杉。


至此,我們便找到了guid搁料。接下來我們將考慮如何用程序模擬用戶點擊按鈕操作或详,并獲取快遞員相關公開信息系羞。

2.1.3 如何獲取guid?

顯然通過HTTP請求獲取源碼解析是不可行的霸琴,在此采用模擬瀏覽器椒振,Python中的selenium就是一種使用瀏覽器內(nèi)核去訪問鏈接,跟真正的瀏覽器訪問頁面沒有什么差別沈贝,并且我們可以通過解析得到guid杠人,至于selenium的環(huán)境配置在此就不詳細講解了,具體可搜索解決宋下。

實際上便是模擬瀏覽行為:打開瀏覽器-->輸入快遞100網(wǎng)網(wǎng)址-->輸入要搜索地區(qū)-->點擊按鈕“點擊查看完整號碼”嗡善。

除此之外如要獲取不同地區(qū)(如全廣州市)的快遞員信息,還需地理關鍵詞信息学歧。為此罩引,在網(wǎng)上找到了一份全國的省市區(qū)鎮(zhèn)街道四級聯(lián)動地理位置數(shù)據(jù),入庫后其數(shù)據(jù)表格式如下(在后面只提供了相關接口調(diào)用枝笨,后續(xù)有需要再進行爬仍怼):

地理位置數(shù)據(jù)示例

3. 爬蟲構(gòu)建

3.1 搭建爬蟲環(huán)境

  • Windows 8.1
  • Python 3.5

導入Python第三方庫。

# 1横浑、HTTP請求
import urllib.request  #請求
import urllib.parse  #URL編碼
import time  #設置延時
from multiprocessing.dummy import Pool  #多線程
import random
# 2剔桨、模擬瀏覽器
from selenium import webdriver
from selenium.webdriver.common.by import By
# 3、數(shù)據(jù)解析
import json  #json格式解析
from lxml import etree  #解析為XML和HTML
import re  #正則匹配
# 4徙融、數(shù)據(jù)存儲
import MySQLdb

3.2 瀏覽器偽裝

def getUserAgent():
    '''
    功能:隨機獲取HTTP_User_Agent
    '''
    user_agents=[
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
    "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
    "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
    "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
    "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
    "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
    "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
    "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
    "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
    "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
    "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
    "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
    ]
    user_agent = random.choice(user_agents)
    return user_agent

3.3 構(gòu)建代理IP池

def getProxies(pages):
    '''
    功能:爬取西刺高匿IP構(gòu)造原始代理IP池
    @pages:獲取多少頁原始代理IP
    '''
    init_proxies = []
    ##爬取前十頁
    for i in range(1,pages+1):
        print("####")
        print("####爬取第"+str(i)+"頁####")
        print("####")        
        print("IP地址\t\t\t端口\t存活時間\t\t驗證時間")
        url = "http://www.xicidaili.com/nn/"+str(i)
        user_agent = getUserAgent()
        headers=("User-Agent",user_agent)
        opener = urllib.request.build_opener() 
        opener.addheaders = [headers] 
        try:
            data = opener.open(url,timeout=5).read()
        except Exception as er:
            print("爬取的時候發(fā)生錯誤洒缀,具體如下:")
            print(er)
        selector=etree.HTML(data) 
        ip_addrs = selector.xpath('//tr[@class="odd"]/td[2]/text()')  #IP地址
        port = selector.xpath('//tr[@class="odd"]/td[3]/text()')  #端口
        sur_time = selector.xpath('//tr[@class="odd"]/td[9]/text()')  #存活時間
        ver_time = selector.xpath('//tr[@class="odd"]/td[10]/text()')  #驗證時間
        for j in range(len(ip_addrs)):
            ip = ip_addrs[j]+":"+port[j] 
            init_proxies.append(ip)
            print(ip_addrs[j]+"\t\t"+port[j]+"\t\t"+sur_time[j]+"\t"+ver_time[j])#輸出爬取數(shù)據(jù) 
    return init_proxies

    
def testProxy(curr_ip):
    '''
    功能:驗證IP有效性
    @curr_ip:當前被驗證的IP
    '''
    tmp_proxies = []
    tarURL = "http://www.baidu.com/" 
    user_agent = getUserAgent()
    proxy_support = urllib.request.ProxyHandler({"http":curr_ip})
    opener = urllib.request.build_opener(proxy_support)
    opener.addheaders=[("User-Agent",user_agent)]
    urllib.request.install_opener(opener)
    try:
        res = urllib.request.urlopen(tarURL,timeout=5).read()
        if len(res)!=0:
            tmp_proxies.append(curr_ip)
    except urllib.error.URLError as er2: 
        if hasattr(er2,"code"):
            print("驗證代理IP("+curr_ip+")時發(fā)生錯誤(錯誤代碼):"+str(er2.code))
        if hasattr(er2,"reason"):
            print("驗證代理IP("+curr_ip+")時發(fā)生錯誤(錯誤原因):"+str(er2.reason))
    except Exception as er:
        print("驗證代理IP("+curr_ip+")時發(fā)生如下錯誤):")
        print(er)
    return tmp_proxies
        
##2.3 多線程驗證     
def mulTestProxies(unchecked_proxies):
    '''
    功能:多線程驗證IP有效性
    @tmp_proxies:原始代理IP池
    '''
    pool = Pool(processes=3)
    fl_proxies = pool.map(testProxy,unchecked_proxies)
    pool.close()
    pool.join()  #等待進程池中的worker進程執(zhí)行完畢
    return fl_proxies

3.4 廣東高校數(shù)據(jù)獲取

中國高校之窗上的數(shù)據(jù)為例進行爬取與解析,該網(wǎng)站的HTML文件不是完全規(guī)范化與標準化欺冀,所以解析要注意下树绩,在此就不一一解釋了,直接看代碼隐轩。

def getSchoolInfo():
    '''
    功能:獲取廣東高校信息
    '''
    url = "http://www.gx211.com/gxmd/gx-gd.html"
    user_agent = getUserAgent()
    headers=("User-Agent",user_agent)
    opener = urllib.request.build_opener() 
    opener.addheaders = [headers] 
    try:
        data = opener.open(url,timeout=5).read()
    except Exception as er:
        print("爬取的時候發(fā)生錯誤饺饭,具體如下:")
        print(er)
    ####解析數(shù)據(jù)(非規(guī)整的html文件)
    selector = etree.HTML(data)
    school_name_list1 = selector.xpath('//div[@id!="Div0"]/table/tbody/tr/td[1]')
    school_name_list2 = selector.xpath('//div[@id="Div3"]/table/tr/td[1]')
        
    school_zhuguan_list1 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tbody/tr/td[2]/text()')
    school_zhuguan_list2 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tr/td[2]/text()')
    
    school_loc_list1 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tbody/tr/td[3]/text()')
    school_loc_list2 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tr/td[3]/text()')

    school_cengci_list1 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tbody/tr/td[4]/text()')
    school_cengci_list2 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tr/td[4]/text()')
    
    school_leixing_list1 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tbody/tr/td[5]/text()')
    school_leixing_list2 = selector.xpath('//div[@class="WrapContent"]/div[@id!="Div0"]/table/tr/td[5]/text()') 
    
    school_name_list = school_name_list1+school_name_list2
    school_zhuguan_list = school_zhuguan_list1+school_zhuguan_list2
    school_loc_list = school_loc_list1+school_loc_list2
    school_cengci_list = school_cengci_list1+school_cengci_list2
    school_leixing_list = school_leixing_list1+school_leixing_list2
    ####存儲數(shù)據(jù)
    school_info = [['學校名稱','主管部門','所在地','層次','類型']]
    for j in range(len(school_name_list)):
        school_name = "/".join(school_name_list[j].xpath('descendant-or-self::text()'))#選取當前節(jié)點的所有后代元素(子、孫等)以及當前節(jié)點本身 
        school_name.replace('-','')
        school_name = re.search(u'[\u4e00-\u9fa5]+',school_name).group()#正則匹配中文
        school_zhuguan = (school_zhuguan_list[j]).strip()
        school_loc = school_loc_list[j].strip()
        school_cengci = school_cengci_list[j].strip()
        school_leixing = school_leixing_list[j].strip()
        if school_name!='學校名稱' or school_zhuguan!='主管部門' or school_loc!='所在地' or school_cengci!='層次' or school_leixing!='類型':
            school_info.append([school_name,
                                school_zhuguan,
                                school_loc,
                                school_cengci,
                                school_leixing                  
                                ])
    return school_info

3.5 數(shù)據(jù)獲取與解析

數(shù)據(jù)的獲取分為兩步职车,先通過selenium模擬瀏覽器解析得到數(shù)據(jù)XHR文件的guid瘫俊,再通過構(gòu)建XHR的URL進行訪問并解析,注意代理IP池的更新與爬取頻率悴灵,快遞100網(wǎng)的反爬蟲機制還是比較敏感的扛芽,所以在爬的時候頻率采用隨機小睡眠,定量大休眠的方式称勋,具體見代碼。

def getGuids(keywords):
    '''
    功能:獲取數(shù)據(jù)
    @keywords:搜索關鍵詞
    '''
    guids = []
    pat = "\(\'(.*?)\'\)\;"  #ID匹配模式
    chromedriver = "C:/Users/whenif/AppData/Local/Google/Chrome/Application/chromedriver"
    i = 0
    j = 0
    for keyword in keywords:
        i += 1
        browser = webdriver.Chrome(chromedriver)  #模擬瀏覽器
        keyword = urllib.parse.quote(keyword) #URL編碼
        browser.get("https://www.kuaidi100.com/courier/?searchText="+keyword)
        ids = browser.find_elements(by=By.XPATH,value="http://div[@id='queryResult']/dl/dd[2]/span/a")  #構(gòu)建XHR的ID
        print("正在爬取第"+str(i)+"個關鍵詞...")
        if i==80:
            time.sleep(180)#每爬取80個休息3分鐘
        else:
            seconds =  random.randint(8, 12)
            time.sleep(seconds)
        for id in ids:
            j += 1  #調(diào)試
            print("共爬取到"+str(j)+"個guid...")
            id = id.get_attribute('onclick')
            id = re.compile(pat).findall(id)
            guids.append([urllib.parse.unquote(keyword),id[0]])
        browser.quit()
    return guids


def getInfos(guids,proxy_pool):
    '''
    功能:獲取數(shù)據(jù)
    @guids:快遞員全局唯一標識列表
    @proxy_pool:代理IP池
    '''
    global data 
    infos = []#存儲最終所有信息
    i = 0  #代理IP循環(huán)調(diào)度累加器
    j = 1  #爬取個數(shù)累加器
    for guid in guids:
        URL = 'https://www.kuaidi100.com/courier/searchapi.do?method=courierdetail&json={"guid":"'+guid[1]+'"}'  #數(shù)據(jù)所在URL
        user_agent = getUserAgent()
        my_user_agent = ("User-Agent",user_agent)
        print("正在爬取第"+str(j)+"個快遞員信息...")
        j += 1 
        i += 1
        if len(proxy_pool)!=0 and i < len(proxy_pool):
            i=i
        elif len(proxy_pool)!=0 and i >= len(proxy_pool):
            i=0
        else:
            print("代理IP池資源已枯竭涯竟,正在更新代理IP池...")
            unchecked_proxies = getProxies(5)  #獲取原始代理IP
            checked_proxies = mulTestProxies(unchecked_proxies)#多線程測試原始代理IP   
            proxy_pool = []
            for tmp_proxy in checked_proxies:
                if len(tmp_proxy)!=0:
                    proxy_pool.append(tmp_proxy)
            print("代理IP池更新完畢赡鲜,共獲取"+str(len(proxy_pool))+"個代理IP")
            i=0 
        proxy_addr = proxy_pool[i]
        proxy = urllib.request.ProxyHandler({"http":proxy_addr[0]})
        opener = urllib.request.build_opener(proxy,urllib.request.HTTPHandler)
        opener.addheaders=[my_user_agent]
        urllib.request.install_opener(opener)
        try:
            data = opener.open(URL).read() 
        except urllib.error.URLError as er2: 
            proxy_pool.remove(proxy_addr) #報錯則移除改IP
            if hasattr(er2,"code"):
                print("錯誤代碼:"+str(er2.code))
            if hasattr(er2,"reason"):
                print("錯誤原因:"+str(er2.reason))
        if type(data)==str:
            data = data
        else:
            data = data.decode()
        data_json = json.loads(data)
        company_name = data_json['courier']['companyName']  #公司名稱
        courier_name = data_json['courier']['courierName']  #快遞員姓名
        courier_tel = data_json['courier']['courierTel']  #手機號碼
        work_time = data_json['courier']['workTime']  #工作時間
        score = data_json['courier']['score']  #得分
        xzq_full_name = data_json['courier']['xzqFullName']  #地區(qū)
        infos.append([guid[0],guid[1],company_name,courier_name,courier_tel,work_time,score,xzq_full_name])
        # 控制爬取頻率空厌,每100次爬取休息1分鐘的,其他的每次爬取休息3秒
        if i%100==0:
            time.sleep(60)
        else:
            time.sleep(3)
    return infos

3.6 數(shù)據(jù)存儲

def dbCon():
    '''
    功能:連接MySQL數(shù)據(jù)庫
    '''
    con = MySQLdb.connect(
        host='localhost',  # port
        user='***',       # usr_name
        passwd='***',     # passname
        db='***',  # db_name
        charset='utf8',
        local_infile = 1
        )
    return con  
 
def exeSQL(sql):
    '''
    功能:數(shù)據(jù)庫查詢函數(shù) 
    @sql:定義SQL語句
    '''
    global res
    print("exeSQL: " + sql)
    #連接數(shù)據(jù)庫
    con = dbCon()  #創(chuàng)建數(shù)據(jù)庫的連接
    cur = con.cursor()  #通過獲取到的數(shù)據(jù)庫連接conn下的cursor()方法來創(chuàng)建游標
    try:
        tmp = cur.execute(sql) #通過游標cur 操作execute()方法可以寫入純sql語句
        res = cur.fetchmany(tmp)#cur.fetchone()只會使游標不斷的向下移動
    except Exception as er:
        print('執(zhí)行MySQL語句【' + str(sql) + '】時出如下錯誤:')        
        print(er)
    finally:
        cur.close()  #關閉游標
        con.commit()  #方法在提交事物银酬,在向數(shù)據(jù)庫插入一條數(shù)據(jù)時必須要有這個方法嘲更,否則數(shù)據(jù)不會被真正的插入。
        con.close()  #關閉數(shù)據(jù)庫連接
    return res
    
def exeInsertSQL(sql,data_list):
    '''
    功能:數(shù)據(jù)庫插入函數(shù) 
    @sql:定義插入SQL語句
    @data_list:插入數(shù)據(jù)列表
    ''' 
    con = dbCon()  #創(chuàng)建數(shù)據(jù)庫的連接
    cur = con.cursor()
    try:
        n = cur.executemany(sql,data_list)
    except Exception as er:
        print('執(zhí)行MySQL語句【' + str(sql) + '】時出如下錯誤:')        
        print(er)
    finally:
        cur.close()  #關閉游標
        con.commit()  #方法在提交事物揩瞪,在向數(shù)據(jù)庫插入一條數(shù)據(jù)時必須要有這個方法赋朦,否則數(shù)據(jù)不會被真正的插入。
        con.close()  #關閉數(shù)據(jù)庫連接

def dataStore(school_info,xhr_guids,courier_info):
    '''
    功能:數(shù)據(jù)庫存儲 
    @school_info:學校信息
    @xhr_guids:數(shù)據(jù)XHR文件的全局唯一標識符
    @courier_info:快遞員數(shù)據(jù)
    ''' 
    #存儲學校信息
    table_name1 = 'school_info'
    exeSQL("drop table if exists " + table_name1)
    exeSQL("create table " + table_name1 + "(學校名稱 varchar(100), 主管部門 varchar(50), 所在地 varchar(50), 層次 varchar(50), 類型 varchar(50));")
    insert_sql1 = "insert into " + table_name1 + " values(%s,%s,%s,%s,%s);"
    exeInsertSQL(insert_sql1,school_info)       
    #存儲數(shù)據(jù)XHR文件的全局唯一標識符
    table_name2 = 'xhr_guids'
    exeSQL("drop table if exists " + table_name2)
    exeSQL("create table " + table_name2 + "(搜索關鍵詞 varchar(100),全局標識符 varchar(50));")   
    insert_sql2 = "insert into " + table_name2 + " values(%s,%s);"
    exeInsertSQL(insert_sql2,xhr_guids)
    #存儲快遞員數(shù)據(jù)
    table_name3 = 'courier_info'
    exeSQL("drop table if exists " + table_name3)
    exeSQL("create table " + table_name3 + "(`搜索關鍵詞` varchar(100),`全局標識符` varchar(50),`所屬公司` varchar(50),`快遞員姓名` varchar(20),`手機號碼` varchar(20),`工作時間` varchar(100),`得分` varchar(30),`所屬地區(qū)` varchar(50));")   
    insert_sql3 = "insert into " + table_name3 + " values(%s,%s,%s,%s,%s,%s,%s,%s);"
    exeInsertSQL(insert_sql3,courier_info)
 

3.7 主函數(shù)調(diào)用

def main():
    '''
    功能:主函數(shù)李破,調(diào)用相關函數(shù)    
    '''
    #---(1)獲取初始代理IP池
    unchecked_proxies = getProxies(10)  #獲取原始代理IP
    checked_proxies = mulTestProxies(unchecked_proxies)  #多線程測試原始代理IP   
    proxy_pool = []
    for tmp_proxy in checked_proxies:
        if len(tmp_proxy)!=0:
            proxy_pool.append(tmp_proxy)
            print("代理IP池獲取完畢宠哄,共獲取"+str(len(proxy_pool))+"個代理IP")
    #---(2)獲取地理位置(搜索關鍵詞)
    school_info = getSchoolInfo()  #獲取全省高校信息
    final_loc = []  #提取搜索關鍵詞
    for loc in school_info[1:]:
        final_loc.append(loc[0])
    '''預留全省街道信息獲取接口
    sel_sql = 'select `province_name`\
                    ,`city_name`\
                    ,`county_name`\
                    ,`town_name`\
                    ,`village_name`\
            from    positionV1 ' +\
            'where  `province_name`="廣東省" \
               and  city_name="廣州市";'
    location = exeSQL(sel_sql)
    final_loc = []
    for loc in location:
        loc = loc[1]+loc[2]+loc[3]+loc[4]
        final_loc.append(loc)'''
    #---(3)獲取數(shù)據(jù)
    xhr_guids = getGuids(final_loc)
    courier_info = getInfos(guids,proxy_pool) #爬取快遞號碼信息
    #---(4)存儲數(shù)據(jù)
    dataStore(school_info,xhr_guids,courier_info)
  
if __name__ == "__main__":
    main()

4 結(jié)果與可視化

4.1 結(jié)果表結(jié)構(gòu)

爬取數(shù)據(jù)并入庫之后結(jié)果如下:


廣東大學信息表

信息表

該結(jié)果只是簡單利用學校名稱進行搜索得到的結(jié)果,同一學校不同分校區(qū)均劃分算一學校嗤攻,如中山大學有東校區(qū)和南校區(qū)毛嫉,所以結(jié)果會有一定誤差。如要獲取全廣州快遞號碼妇菱,可以通過調(diào)用四級聯(lián)動街道數(shù)據(jù)庫接口進行查詢承粤。

4.2 數(shù)據(jù)可視化

該爬蟲共爬取快遞員信息2052條,利用快遞員號碼進行剔重之后剩下1469條闯团,學行岭總共有142個(本科62、尫拷唬科80)彻舰,每個學校平均大約有10個快遞員。

(1)快遞員公司分布

(2)快遞員區(qū)域分布

全省各地市高杏坑快遞員分布

廣州高醒妥瘢快遞員分布

全省高校快遞員數(shù)量前三名為廣州负溪、東莞透揣、佛山,廣州市內(nèi)前三名分別是天河川抡、白云和番禺辐真。

(3)快遞員高校分布

以上結(jié)果很大程度上取決與不同分類學校的數(shù)量,如廣州的高校數(shù)量肯定大于其他地市崖堤,所以爬取結(jié)果的快遞員數(shù)量肯定多于其他地市侍咱,其他同理。但是值得注意的是快遞員手機號碼是有很重要的特征密幔,如打出次數(shù)遠超打入次數(shù)楔脯,平均通話時長較短、發(fā)送信息量較多等胯甩,從運營商數(shù)據(jù)角度看昧廷,快遞員如同中介作為一種特殊的人群堪嫂,其號碼群對于模型的建立與校驗還是有一定價值的。


本文所有代碼只用于技術交流木柬,拒絕任何商用活動
文章相關項目代碼已上傳至個人Github
個人博客DebugNLP
歡迎各路同學互相交流

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皆串,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子眉枕,更是在濱河造成了極大的恐慌恶复,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件速挑,死亡現(xiàn)場離奇詭異谤牡,居然都是意外死亡,警方通過查閱死者的電腦和手機梗摇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門拓哟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來圆雁,“玉大人腾誉,你說我怎么就攤上這事〔鸹樱” “怎么了糜烹?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵违诗,是天一觀的道長。 經(jīng)常有香客問我疮蹦,道長诸迟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任愕乎,我火速辦了婚禮阵苇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘感论。我一直安慰自己绅项,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布比肄。 她就那樣靜靜地躺著快耿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芳绩。 梳的紋絲不亂的頭發(fā)上掀亥,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音妥色,去河邊找鬼搪花。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的撮竿。 我是一名探鬼主播丁稀,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倚聚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起凿可,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤惑折,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后枯跑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惨驶,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年敛助,在試婚紗的時候發(fā)現(xiàn)自己被綠了粗卜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡纳击,死狀恐怖续扔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情焕数,我是刑警寧澤纱昧,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站堡赔,受9級特大地震影響识脆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜善已,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一灼捂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧换团,春花似錦悉稠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辑甜,卻和暖如春衰絮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背磷醋。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工猫牡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邓线。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓淌友,卻偏偏與公主長得像煌恢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子震庭,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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