目錄
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ù)有需要再進行爬仍怼):
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
歡迎各路同學互相交流