目錄
1.“看不見”的數據
2. 讓數據現身
2.1 數據API分析
2.1.1 數據文件在哪里?
2.1.2 guid(數據全局唯一標識符)在哪里?
2.2.3 如何獲取guid?
3. 爬蟲構建
3.1 搭建爬蟲環境
3.2 瀏覽器偽裝
3.3 構建代理IP池
3.4 廣東高校數據獲取
3.5 數據獲取與解析
3.6 數據存儲
3.7 主函數
4. 結果與可視化
4.1 結果表結構
4.2 數據可視化
1.“看不見”的數據
反爬機制層出不窮,其中很重要的一種手法是對數據進行隱藏,即在源碼中看不到實際的數據,主要手段如下:
- 將數據存儲于json/XHR等文件中,如拉勾網、京東商城價格信息等;
- 直接隱藏數據,需要點擊才能查看完整數據,如快遞100網等
- ......
2.讓數據現身
本章節以爬取快遞100網快遞員手機號碼、公司與歸屬地等信息為例。
2.1 數據API分析
快遞100是一個集快遞單號查詢、快遞單號短信跟蹤、快遞網點查詢、網上寄快遞等為一體的綜合性快遞物流服務網站。其上面的某些較為敏感信息,如快遞員手機號碼的反扒機制是采用隱藏信息的方式,如搜索金蝶大廈附近的快遞員,結果如下圖:
用戶只有通過按鈕“點擊查看完整號碼”才能看到完整號碼,即使點擊查看完整號碼之后在源碼中仍不能看到號碼所在,很明顯其數據已經被隱藏,我們可以猜測其數據可能被存儲于XHR或者js文件中,接下來我們的目的便在研究數據到底通過哪種方式隱藏起來?我們該怎么獲???
2.1.1 數據文件在哪里?
(1)啟動Chrome瀏覽器
打開快遞100搜索頁面,并按F12打開開發環境;
(2)如下圖選定XHR
(3)點擊按鈕“點擊查看完整號碼”
我們可以看到其產生了一個XHR文件,這個很可能便是數據所在。打開該文件,如下圖,這果然便是我們數據所在。
可以看到數據的URL如下,構造該XHR的關鍵就在于后面的guid值(唯一標識符)。所以問題便轉換為如何找到每個搜索關鍵詞的guid?
https://www.kuaidi100.com/courier/searchapi.do?method=courierdetail&json={%22guid%22:%22399F251E9C6C464FCDC93A73C93BD391%22}
2.1.2 guid(數據全局唯一標識符)在哪里?
在尋找guid之前,我們先看一下該網站的用戶行為觸發操作,即當用戶點擊“查看完整號碼”時候網頁將產生什么?
(1)查看網頁源碼
我們點擊“查看完整號碼”之后,右擊查看源碼并在源碼中搜索該號碼,納尼,沒能找到號碼,就連收件地址也找不到。很明顯,網頁是將相關信息給隱藏起來了。
在此必須明白所謂源碼,就是網站服務器發送到瀏覽器的原封不動的代碼,上面我們又知道每次點擊“查看完整號碼”的時候會產生一個XHR文件并且頁面的號碼也全部顯示出來,瀏覽器執行js動態肯定會產生新的HTML代碼,我們在源碼中找不到的代碼,但通過審查元素便能看到最終的HTML代碼。我們接下來我們通過審查元素進行分析。
(2)檢查元素
將鼠標移至“點擊查看完整號碼”上面,點擊右鍵檢查。
至此,我們便找到了guid。接下來我們將考慮如何用程序模擬用戶點擊按鈕操作,并獲取快遞員相關公開信息。
2.1.3 如何獲取guid?
顯然通過HTTP請求獲取源碼解析是不可行的,在此采用模擬瀏覽器,Python中的selenium就是一種使用瀏覽器內核去訪問鏈接,跟真正的瀏覽器訪問頁面沒有什么差別,并且我們可以通過解析得到guid,至于selenium的環境配置在此就不詳細講解了,具體可搜索解決。
實際上便是模擬瀏覽行為:打開瀏覽器-->輸入快遞100網網址-->輸入要搜索地區-->點擊按鈕“點擊查看完整號碼”。
除此之外如要獲取不同地區(如全廣州市)的快遞員信息,還需地理關鍵詞信息。為此,在網上找到了一份全國的省市區鎮街道四級聯動地理位置數據,入庫后其數據表格式如下(在后面只提供了相關接口調用,后續有需要再進行爬取):
3. 爬蟲構建
3.1 搭建爬蟲環境
- 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、數據解析
import json #json格式解析
from lxml import etree #解析為XML和HTML
import re #正則匹配
# 4、數據存儲
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 構建代理IP池
def getProxies(pages):
'''
功能:爬取西刺高匿IP構造原始代理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("爬取的時候發生錯誤,具體如下:")
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])#輸出爬取數據
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+")時發生錯誤(錯誤代碼):"+str(er2.code))
if hasattr(er2,"reason"):
print("驗證代理IP("+curr_ip+")時發生錯誤(錯誤原因):"+str(er2.reason))
except Exception as er:
print("驗證代理IP("+curr_ip+")時發生如下錯誤):")
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進程執行完畢
return fl_proxies
3.4 廣東高校數據獲取
以中國高校之窗上的數據為例進行爬取與解析,該網站的HTML文件不是完全規范化與標準化,所以解析要注意下,在此就不一一解釋了,直接看代碼。
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("爬取的時候發生錯誤,具體如下:")
print(er)
####解析數據(非規整的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
####存儲數據
school_info = [['學校名稱','主管部門','所在地','層次','類型']]
for j in range(len(school_name_list)):
school_name = "/".join(school_name_list[j].xpath('descendant-or-self::text()'))#選取當前節點的所有后代元素(子、孫等)以及當前節點本身
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 數據獲取與解析
數據的獲取分為兩步,先通過selenium模擬瀏覽器解析得到數據XHR文件的guid,再通過構建XHR的URL進行訪問并解析,注意代理IP池的更新與爬取頻率,快遞100網的反爬蟲機制還是比較敏感的,所以在爬的時候頻率采用隨機小睡眠,定量大休眠的方式,具體見代碼。
def getGuids(keywords):
'''
功能:獲取數據
@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") #構建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 #調試
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):
'''
功能:獲取數據
@guids:快遞員全局唯一標識列表
@proxy_pool:代理IP池
'''
global data
infos = []#存儲最終所有信息
i = 0 #代理IP循環調度累加器
j = 1 #爬取個數累加器
for guid in guids:
URL = 'https://www.kuaidi100.com/courier/searchapi.do?method=courierdetail&json={"guid":"'+guid[1]+'"}' #數據所在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'] #地區
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 數據存儲
def dbCon():
'''
功能:連接MySQL數據庫
'''
con = MySQLdb.connect(
host='localhost', # port
user='***', # usr_name
passwd='***', # passname
db='***', # db_name
charset='utf8',
local_infile = 1
)
return con
def exeSQL(sql):
'''
功能:數據庫查詢函數
@sql:定義SQL語句
'''
global res
print("exeSQL: " + sql)
#連接數據庫
con = dbCon() #創建數據庫的連接
cur = con.cursor() #通過獲取到的數據庫連接conn下的cursor()方法來創建游標
try:
tmp = cur.execute(sql) #通過游標cur 操作execute()方法可以寫入純sql語句
res = cur.fetchmany(tmp)#cur.fetchone()只會使游標不斷的向下移動
except Exception as er:
print('執行MySQL語句【' + str(sql) + '】時出如下錯誤:')
print(er)
finally:
cur.close() #關閉游標
con.commit() #方法在提交事物,在向數據庫插入一條數據時必須要有這個方法,否則數據不會被真正的插入。
con.close() #關閉數據庫連接
return res
def exeInsertSQL(sql,data_list):
'''
功能:數據庫插入函數
@sql:定義插入SQL語句
@data_list:插入數據列表
'''
con = dbCon() #創建數據庫的連接
cur = con.cursor()
try:
n = cur.executemany(sql,data_list)
except Exception as er:
print('執行MySQL語句【' + str(sql) + '】時出如下錯誤:')
print(er)
finally:
cur.close() #關閉游標
con.commit() #方法在提交事物,在向數據庫插入一條數據時必須要有這個方法,否則數據不會被真正的插入。
con.close() #關閉數據庫連接
def dataStore(school_info,xhr_guids,courier_info):
'''
功能:數據庫存儲
@school_info:學校信息
@xhr_guids:數據XHR文件的全局唯一標識符
@courier_info:快遞員數據
'''
#存儲學校信息
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)
#存儲數據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)
#存儲快遞員數據
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),`所屬地區` varchar(50));")
insert_sql3 = "insert into " + table_name3 + " values(%s,%s,%s,%s,%s,%s,%s,%s);"
exeInsertSQL(insert_sql3,courier_info)
3.7 主函數調用
def main():
'''
功能:主函數,調用相關函數
'''
#---(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)獲取數據
xhr_guids = getGuids(final_loc)
courier_info = getInfos(guids,proxy_pool) #爬取快遞號碼信息
#---(4)存儲數據
dataStore(school_info,xhr_guids,courier_info)
if __name__ == "__main__":
main()
4 結果與可視化
4.1 結果表結構
爬取數據并入庫之后結果如下:
該結果只是簡單利用學校名稱進行搜索得到的結果,同一學校不同分校區均劃分算一學校,如中山大學有東校區和南校區,所以結果會有一定誤差。如要獲取全廣州快遞號碼,可以通過調用四級聯動街道數據庫接口進行查詢。
4.2 數據可視化
該爬蟲共爬取快遞員信息2052條,利用快遞員號碼進行剔重之后剩下1469條,學??偣灿?42個(本科62、???0),每個學校平均大約有10個快遞員。
(1)快遞員公司分布
(2)快遞員區域分布
全省高校快遞員數量前三名為廣州、東莞、佛山,廣州市內前三名分別是天河、白云和番禺。
(3)快遞員高校分布
以上結果很大程度上取決與不同分類學校的數量,如廣州的高校數量肯定大于其他地市,所以爬取結果的快遞員數量肯定多于其他地市,其他同理。但是值得注意的是快遞員手機號碼是有很重要的特征,如打出次數遠超打入次數,平均通話時長較短、發送信息量較多等,從運營商數據角度看,快遞員如同中介作為一種特殊的人群,其號碼群對于模型的建立與校驗還是有一定價值的。
本文所有代碼只用于技術交流,拒絕任何商用活動
文章相關項目代碼已上傳至個人Github
個人博客DebugNLP
歡迎各路同學互相交流