從今天開始,咱們嘗試用2篇博客的內容量,搞定一個網站叫作「美空網」網址爲:http://www.moko.cc/, 這個網站我分析了一下,咱們要爬取的圖片在 下面這個網址html
http://www.moko.cc/post/13020...
而後在去分析一下,我須要找到一個圖片列表頁面是最好的,做爲一個勤勞的爬蟲coder,我找到了這個頁面python
http://www.moko.cc/post/da39d...
列表頁面被我找到了,貌似沒有分頁,這就簡單多了,可是剛想要爬,就翻車了,我發現一個嚴重的問題。linux
http://www.moko.cc/post/==da3...git
我要作的是一個自動化的爬蟲,可是我發現,出問題了,上面那個黃色背景的位置是啥?github
ID,暱稱,個性首頁,這個必需要搞定。正則表達式
我接下來隨機的找了一些圖片列表頁,試圖找到規律究竟是啥?mongodb
沒什麼問題,發現規律了shell
http://www.moko.cc/post/==個...數據庫
這就有點意思了,我要是能找到儘可能多的暱稱,不就能拼接出來我想要得全部地址了嗎api
開幹!!!
手段,全站亂點,找入口,找切入點,找是否有API
.... .... 結果沒找着
下面的一些備選方案
趴這個頁面,發現只有 20頁 http://www.moko.cc/channels/p...
每頁48個模特,20頁。那麼也才960人啊,徹底覆蓋不到儘量多的用戶。
接着又找到
http://www.moko.cc/catalog/in... 這個頁面
確認了一下眼神,覺得發現問題了,結果
哎呀,還麼有權限,誰有權限,能夠跟我交流一下,一時激動,差點去下載他們的APP,而後進行抓包去。
上面兩條路,都很差弄,接下來繼續找路子。
無心中,我看到了一絲曙光
關注名單,點進去
哈哈哈,OK了,這不就是,我要找到的東西嗎?
很少說了,爬蟲走起,測試一下他是否有反扒機制。
我找到了一個關注的人比較多的頁面,1500多我的
http://www.moko.cc/subscribe/...
而後又是一波分析操做
肯定了爬蟲的目標,接下來,我作了兩件事情,看一下,是否對你也有幫助
對此,咱們須要安裝一下MongoDB,安裝的辦法確定是官網教程啦!
https://docs.mongodb.com/mast...
若是官方文檔沒有幫助你安裝成功。
那麼我推薦下面這篇博客
https://www.cnblogs.com/hacky...
安裝MongoDB出現以下結果
恭喜你安裝成功了。
接下來,你要學習的是 關於mongodb用戶權限的管理
http://www.cnblogs.com/shiyiw...
mongodb索引的建立
https://blog.csdn.net/salmone...
別問爲啥我不從新寫一遍,懶唄~~~ 何況這些資料太多了,互聯網大把大把的。
一些我常常用的mongdb的命令
連接 mongo --port <端口號> 選擇數據庫 use admin 展現當前數據庫 db 當前數據庫受權 db.auth("用戶名","密碼") 查看數據庫 show dbs 查看數據庫中的列名 show collections 建立列 db.createCollection("列名") 建立索引 db.col.ensureIndex({"列名字":1},{"unique":true}) 展現全部索引 db.col.getIndexes() 刪除索引 db.col.dropIndex("索引名字") 查找數據 db.列名.find() 查詢數據總條數 db.列名.find().count()
上面基本是我最經常使用的了,咱們下面實際操做一把。
使用 pip3 安裝pymongo庫
使用pymongo模塊鏈接mongoDB數據庫
一些準備工做
連接上mongodb 在終端使用命令 mongo --port 21111
[linuxboy@localhost ~]$ mongo --port 21111 MongoDB shell version v3.6.5 connecting to: mongodb://127.0.0.1:21111/ MongoDB server version: 3.6.5 >
權限不足
db.createUser({user: "userAdmin",pwd: "123456", roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] } )
db.auth("userAdmin","123456")
> db.auth("userAdmin","123456") 1 > show dbs admin 0.000GB config 0.000GB local 0.000GB moko 0.013GB test 0.000GB >
> use dm switched to db dm > db dm > db.createUser({user: "dba",pwd: "dba", roles: [ { role: "readWrite", db: "dm" } ] } ) Successfully added user: { "user" : "dba", "roles" : [ { "role" : "readWrite", "db" : "dm" } ] } >
db.auth("dba","dba")
> db.createCollection("demo") { "ok" : 1 } > db.collections dm.collections > show collections demo >
import pymongo as pm #確保你已經安裝過pymongo了 # 獲取鏈接 client = pm.MongoClient('localhost', 21111) # 端口號是數值型 # 鏈接目標數據庫 db = client.dm # 數據庫用戶驗證 db.authenticate("dba", "dba") post = { "id": "111111", "level": "MVP", "real":1, "profile": '111', 'thumb':'2222', 'nikename':'222', 'follows':20 } db.col.insert_one(post) # 插入單個文檔 # 打印集合第1條記錄 print (db.col.find_one())
[linuxboy@bogon moocspider]$ python3 mongo.py {'_id': ObjectId('5b15033cc3666e1e28ae5582'), 'id': '111111', 'level': 'MVP', 'real': 1, 'profile': '111', 'thumb': '2222', 'nikename': '222', 'follows': 20} [linuxboy@bogon moocspider]$
好了,咱們到如今爲止,實現了mongodb的插入問題。
首先,我須要創造一個不斷抓取連接的類
這個類作的事情,就是分析
http://www.moko.cc/subscribe/...
這個頁面,總共有多少頁,而後生成連接
抓取頁面中的總頁數爲77
正則表達式以下
onfocus=\"this\.blur\(\)\">(\d*?)<
在這裏,由全部的分頁都同樣,因此,我匹配了所有的頁碼,而後計算了數組中的最大值
#獲取頁碼數組 pages = re.findall(r'onfocus=\"this\.blur\(\)\">(\d*?)<',content,re.S) #獲取總頁數 page_size = 1 if pages: #若是數組不爲空 page_size = int(max(pages)) #獲取最大頁數
接下來就是咱們要搞定的生產者
編碼階段了,咱們須要打造一個不斷獲取鏈接的爬蟲
簡單的說就是
咱們須要一個爬蟲,不斷的去爬取
http://www.moko.cc/subscribe/... 這個頁面中全部的用戶,而且還要爬取到總頁數。
好比查看上述頁面中,咱們要獲取的關鍵點以下
經過這個頁面,咱們要獲得,這樣子的一個數組,注意下面數組中有個位置【我用爬蟲爬到的】這個就是關鍵的地方了
all_urls = [ "http://www.moko.cc/subscribe/chenhaoalex/1.html", "http://www.moko.cc/subscribe/chenhaoalex/2.html", "http://www.moko.cc/subscribe/chenhaoalex/3.html", "http://www.moko.cc/subscribe/chenhaoalex/4.html", ...... "http://www.moko.cc/subscribe/dde760d5dd6a4413aacb91d1b1d76721/1.html" "http://www.moko.cc/subscribe/3cc82db2231a4449aaa97ed8016b917a/1.html" "http://www.moko.cc/subscribe/d45c1e3069c24152abdc41c1fb342b8f/1.html" "http://www.moko.cc/subscribe/【我用爬蟲爬到的】/1.html" ]
引入必備模塊
# -*- coding: UTF-8 -*- import requests #網絡請求模塊 import random #隨機模塊 import re #正則表達式模塊 import time #時間模塊 import threading #線程模塊 import pymongo as pm #mongodb模塊
接下來,咱們須要準備一個通用函數模擬UserAgent
作一個簡單的反爬處理
class Config(): def getHeaders(self): user_agent_list = [ \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1" \ "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", \ "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", \ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", \ "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", \ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \ "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" ] UserAgent=random.choice(user_agent_list) headers = {'User-Agent': UserAgent} return headers
編寫生產者的類和核心代碼,Producer
繼承threading.Thread
#生產者 class Producer(threading.Thread): def run(self): print("線程啓動...") headers = Config().getHeaders() if __name__ == "__main__": p = Producer() p.start()
測試運行,一下,看是否能夠啓動
[linuxboy@bogon moocspider]$ python3 demo.py 線程啓動... {'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24'} [linuxboy@bogon moocspider]$
若是上面的代碼沒有問題,接下來就是咱們爬蟲代碼部分了,爲了方便多線程之間的調用,咱們仍是建立一個共享變量在N個線程之間調用
# -*- coding: UTF-8 -*- import requests import random import re import time import threading import pymongo as pm # 獲取鏈接 client = pm.MongoClient('localhost', 21111) # 端口號是數值型 # 鏈接目標數據庫 db = client.moko # 數據庫用戶驗證 db.authenticate("moko", "moko") urls = ["http://www.moko.cc/subscribe/chenhaoalex/1.html"] index = 0 #索引 g_lock = threading.Lock() #初始化一個鎖 #生產者 class Producer(threading.Thread): def run(self): print("線程啓動...") headers = Config().getHeaders() print(headers) global urls global index while True: g_lock.acquire() if len(urls)==0: g_lock.release() continue page_url = urls.pop() g_lock.release() #使用完成以後及時把鎖給釋放,方便其餘線程使用 response = "" try: response = requests.get(page_url,headers=headers,timeout=5) except Exception as http: print("生產者異常") print(http) continue content = response.text rc = re.compile(r'<a class=\"imgBorder\" href=\"\/(.*?)\" hidefocus=\"true\">') follows = rc.findall(content) print(follows) fo_url = [] threading_links_2 = [] for u in follows: this_url = "http://www.moko.cc/subscribe/%s/1.html" % u g_lock.acquire() index += 1 g_lock.release() fo_url.append({"index":index,"link":this_url}) threading_links_2.append(this_url) g_lock.acquire() urls += threading_links_2 g_lock.release() print(fo_url) try: db.text.insert_many(fo_url,ordered=False ) except: continue if __name__ == "__main__": p = Producer() p.start()
上面代碼除了基本操做之外,我作了一些細小的處理
如今說明以下
fo_url.append({"index":index,"link":this_url})
這部分代碼,是爲了消費者使用時候,方便進行查找而且刪除操做而特地改造的,增長了一個字段index做爲標識
第二個部分,插入數據的時候,我進行了批量的操做使用的是insert_many
函數,而且關鍵的地方,我增長了一個ordered=False的操做,這個地方你們能夠自行研究一下,個人目的是去掉重複數據,默認狀況下insert_many
函數若是碰到數據重複,而且在mongodb中建立了索引==建立索引的辦法,你們自行翻閱文章上面==,那麼是沒法插入的,可是這樣子會插入一部分,只把重複的地方略過,很是方便。
關於pymongo的使用,你們能夠參考官網手冊
這個是 pymongo的官方教程
http://api.mongodb.com/python...
MongoDB的手冊你們也能夠參考
https://docs.mongodb.com/manu...
db.text.insert_many(fo_url,ordered=False )
咱們連接上MongoDB數據庫,查詢一下咱們剛剛插入的數據
> show collections col links text > db.text moko.text > db.text.find() { "_id" : ObjectId("5b1789e0c3666e642364a70b"), "index" : 1, "link" : "http://www.moko.cc/subscribe/dde760d5dd6a4413aacb91d1b1d76721/1.html" } { "_id" : ObjectId("5b1789e0c3666e642364a70c"), "index" : 2, "link" : "http://www.moko.cc/subscribe/3cc82db2231a4449aaa97ed8016b917a/1.html" } ....... { "_id" : ObjectId("5b1789e0c3666e642364a71e"), "index" : 20, "link" : "http://www.moko.cc/subscribe/8c1e4c738e654aad85903572f9090adb/1.html" } Type "it" for more
其實上面代碼,有一個很是嚴重的BUG,就是當咱們實際操做的時候,發現,咱們每次獲取到的都是咱們使用this_url = "http://www.moko.cc/subscribe/%s/1.html" % u
進行拼接的結果。
也就是說,咱們獲取到的永遠都是第1頁。這個按照咱們以前設計的就不符合邏輯了,
咱們還要獲取到分頁的內容,那麼這個地方須要作一個簡單的判斷,就是下面的邏輯了。
==若是完整代碼,你們不知道如何觀看,能夠直接翻閱到文章底部,有對應的github連接==
#若是是第一頁,那麼須要判斷一下 #print(page_url) is_home =re.search(r'(\d*?)\.html',page_url).group(1) if is_home == str(1): pages = re.findall(r'onfocus=\"this\.blur\(\)\">(\d*?)<',content,re.S) #獲取總頁數 page_size = 1 if pages: page_size = int(max(pages)) #獲取最大頁數 if page_size > 1: #若是最大頁數大於1,那麼獲取全部的頁面 url_arr = [] threading_links_1 = [] for page in range(2,page_size+1): url = re.sub(r'(\d*?)\.html',str(page)+".html",page_url) threading_links_1.append(url) g_lock.acquire() index += 1 g_lock.release() url_arr.append({ "index":index, "link": url}) g_lock.acquire() urls += threading_links_1 # URL數據添加 g_lock.release() try: db.text.insert_many(url_arr,ordered=False ) except Exception as e: print("數據庫輸入異常") print (e) continue else: pass else: pass
截止到如今爲止,其實你已經實現了連接的生產者了 。
咱們在MongoDB中生成了一堆連接,接下來就是使用階段了。
使用起來也是很是簡單。
我先給你們看一個比較複雜的正則表達式
爬蟲寫的好很差,正則表達式站很重要的比例哦~
divEditOperate_(?P<ID>\d*)[\"] .*>[\s\S]*?<p class=\"state\">.*?(?P<級別>\w*P).*</span></span>(?P<是否定證><br/>)?.*?</p>[\s\S]*?<div class=\"info clearfix\">[\s\S]*?<a class=\"imgBorder\" href=\"\/(?P<主頁>.*?)\" hidefocus=\"true\">[\s\S]*?<img .*?src=\"(?P<頭像>.*?)\".*?alt=\".*?\" title=\"(?P<暱稱>.*?)\" />[\s\S]*?<p class=\"font12 lesserColor\">(?P<地點>.*?) .*?<span class=\"font12 mainColor\">(?P<粉絲數目>\d*?)</span>
上面這個正則表達式,就是我爲
http://www.moko.cc/subscribe/chenhaoalex/1.html
這個頁面專門準備的。
這樣子,我就能夠直接獲取到我想要的全部數據了。
消費者的代碼以下
get_index = 0 #消費者類 class Consumer(threading.Thread): def run(self): headers = Config().getHeaders() global get_index while True: g_lock.acquire() get_index += 1 g_lock.release() #從剛纔數據存儲的列裏面獲取一條數據,這裏用到find_one_and_delete方法 #get_index 須要聲明成全局的變量 link = db.links.find_one_and_delete({"index":get_index}) page_url = "" if link: page_url = link["link"] print(page_url+">>>網頁分析中...") else: continue response = "" try: response = requests.get(page_url,headers=headers,timeout=5) except Exception as http: print("消費者有異常") print(http) continue content = response.text rc = re.compile(r'divEditOperate_(?P<ID>\d*)[\"] .*>[\s\S]*?<p class=\"state\">.*?(?P<級別>\w*P).*</span></span>(?P<是否定證><br/>)?.*?</p>[\s\S]*?<div class=\"info clearfix\">[\s\S]*?<a class=\"imgBorder\" href=\"\/(?P<主頁>.*?)\" hidefocus=\"true\">[\s\S]*?<img .*?src=\"(?P<頭像>.*?)\".*?alt=\".*?\" title=\"(?P<暱稱>.*?)\" />[\s\S]*?<p class=\"font12 lesserColor\">(?P<地點>.*?) .*?<span class=\"font12 mainColor\">(?P<粉絲數目>\d*?)</span>') user_info = rc.findall(content) print(">>>>>>>>>>>>>>>>>>>>") users = [] for user in user_info: post = { "id": user[0], "level": user[1], "real":user[2], "profile": user[3], 'thumb':user[4], 'nikename':user[5], 'address':user[6], 'follows':user[7] } users.append(post) print(users) try: db.mkusers.insert_many(users,ordered=False ) except Exception as e: print("數據庫輸入異常") print (e) continue time.sleep(1) print("<<<<<<<<<<<<<<<<<<<<")
當你使用python3 demo.py
編譯demo以後,屏幕滾動以下結果,那麼你成功了。
接下來就能夠去數據庫查閱數據去了。
[linuxboy@bogon moocspider]$ python3 demo.py 線程啓動... {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3'} http://www.moko.cc/subscribe/chenhaoalex/2.html>>>網頁分析中... ['dde760d5dd6a4413aacb91d1b1d76721', '3cc82db2231a4449aaa97ed8016b917a', 'a1835464ad874eec92ccbb31841a7590', 'c9ba6a47a246494398d4e26c1e0b7e54', '902fe175e668417788a4fb5d4de7ab99', 'dcb8f11265594f17b821a6d90caf96a7', '7ea0a96621eb4ed99c9c642936559c94', 'd45c1e3069c24152abdc41c1fb342b8f', 'chenyiqiu', '798522844', 'MEERILLES', 'ddfd9e1f7dca4cffb2430caebd2494f8', 'd19cbd37c87e400e9da42e159560649b', 'ac07e7fbfde14922bb1d0246b9e4374d', '05abc72ac7bb4f738f73028fed17ac23', 'hanzhuoer', 'e12e15aaee654b8aa9f528215bc3294c', '3b6d8dc6fd814789bd484f393b5c9fa8', '83256b93a2f94f449ab75c730cb80a7b', '8c1e4c738e654aad85903572f9090adb'] [{'index': 77, 'link': 'http://www.moko.cc/subscribe/dde760d5dd6a4413aacb91d1b1d76721/1.html'}, {'index': 78, 'link': 'http://www.moko.cc/subscribe/3cc82db2231a4449aaa97ed8016b917a/1.html'}, {'index': 79, 'link': 'http://www.moko.cc/subscribe/a1835464ad874eec92ccbb31841a7590/1.html'}, {'index': 80, 'link': 'http://www.moko.cc/subscribe/c9ba6a47a246494398d4e26c1e0b7e54/1.html'}, {] >>>>>>>>>>>>>>>>>>>> [{'id': '3533155', 'level': 'MP', 'real': '', 'profile': 'b1a7e76455cc4ca4b81ed800ab68b308', 'thumb': 'http://img.mb.moko.cc/2018-02-17/d7db42d4-7f34-46d2-a760-c88eb90d6e0d.jpg', 'nikename': '模特九九', 'address': '大連', 'follows': '10'}, {'id': '3189865', 'level': 'VIP', 'real': '', 'profile': 'cfdf1482a9034f65a60bc6a1cf8d6a02', 'thumb': 'http://img.mb.moko.cc/2016-09-30/98c1ddd3-f9a8-4a15-a106-5d664fa7b558.jpg', 'nikename': '何應77', 'address': '杭州', 'follows': '219'}, {'id': '14886', 'level': 'VIP', 'real': '<br/>', 'profile': 'cndp', 'thumb': 'http://img2.moko.cc/users/0/49/14886/logo/img2_des_x3_10100286.jpg', 'nikename': '多拍PGirl', 'address': '北京', 'follows': '2331'}, {'id': '3539257', 'level': 'MP', 'real': '<br/>', 'profile': '605c8fb2824049aa841f21858a7fd142', 'thumb': 'http://img.mb.moko.cc/2018-02':
記得處理數據的時候去掉重複值
>show collections col links mkusers text > db.mkusers.find() { "_id" : ObjectId("5b17931ec3666e6eff3953bc"), "id" : "3533155", "level" : "MP", "real" : "", "profile" : "b1a7e76455cc4ca4b81ed800ab68b308", "thumb" : "http://img.mb.moko.cc/2018-02-17/d7db42d4-7f34-46d2-a760-c88eb90d6e0d.jpg", "nikename" : "模特九九", "address" : "大連", "follows" : "10" } { "_id" : ObjectId("5b17931ec3666e6eff3953bd"), "id" : "3189865", "level" : "VIP", "real" : "", "profile" : "cfdf1482a9034f65a60bc6a1cf8d6a02", "thumb" : "http://img.mb.moko.cc/2016-09-30/98c1ddd3-f9a8-4a15-a106-5d664fa7b558.jpg", "nikename" : "何應77", "address" : "杭州", "follows" : "219" } { "_id" : ObjectId("5b17931ec3666e6eff3953be"), "id" : "14886", "level" : "VIP", "real" : "<br/>", "profile" : "cndp", "thumb" : "http://img2.moko.cc/users/0/49/14886/logo/img2_des_x3_10100286.jpg", "nikename" : "多拍PGirl", "address" : "北京", "follows" : "2331" } { "_
最後一步,若是你想要把效率提升,修改線程就行了
if __name__ == "__main__": for i in range(5): p = Producer() p.start() for i in range(7): c = Consumer() c.start()
通過3個小時的爬取,我獲取了70000多美空的用戶ID,原則上,你能夠獲取到全部的被關注者的,不過這些數據對咱們測試來講,已經足夠使用。