相信有了前面三章的基礎了解,我們對爬蟲的基礎知識已經有所掌握。 本篇內容是從易到難給大家講解一些常用爬蟲的手寫。 包括圖片爬蟲、鏈接爬蟲、多線程爬蟲等等。
京東圖片爬蟲實戰:
實現目標 :將京東商城手機類的商品圖片全部下載到本地。
首先打開京東首頁。選擇我們要下載的商品分類鏈接。
點擊鏈接之后,我們會看到這樣一個Url 這就是我們要爬取的第一個頁面。 復制下來他的url。https://list.jd.com/list.html?cat=9987,653,655 接下來將頁面拖動到最后可看到 頁面解析:
這里有很多分頁。那么如何獲取其他頁面的信息呢。 我們需要觀察網址的變化:點擊分頁觀察url。 可發現,每頁的字段都有一個page,通過GET的方式請求的。我們可以得到頁面url的關鍵字段: page=2代表著第幾頁。 所以我們可以使用for循環來獲取其他的頁面。
信息提取: 我們需要的是每個頁面的圖片信息。所以需要使用正則表達式來匹配源碼中圖片的鏈接部分。然后通過urllib.request.urlretrieve() 將對應鏈接的圖片保存到本地。
信息過濾: 除了我們想要的手機圖片外,還會匹配到其他跟我們目標不同的圖片。 所以我們要先進行一次信息過濾。只留下我們所需要的。 我們F12進入審查模式 觀察發現圖片都是在 也就是說其實中的<div class="page clearfix">可以作為結束的特殊標識。所以我們進行的第一次過濾,我們的正則表達式可以構造為: pat1='<div id ="plist".+?<div class = "page clearfix">' 每個頁面之間的基本格式是一樣的,只是其中的圖片鏈接網址不一樣,所以此時,我們可以根據該規律構造出我們需要的正則表達式:
'<img width="200" heght="200" data-img="1" data-lazy-img = "//(.+?\.jpg)">'
編寫代碼:
import re #導入re模塊
import urllib.request,urllib.error #導入urllib
def craw(url,page): #定義一個函數html=urllib.request.urlopen(url).read() #獲取頁面信息html=str(html) #將信息轉化為字符串pat1='<div id="plist".+? <div class="page clearfix">'result1=re.compile(pat1).findall(html) #在html1內查詢符合pat1的result1=result1[0] #獲取起始位置的plist,也就是第一個包含了所以圖片的。pat2 ='<img width="220" height="220" data-img="1" data-lazy-img="//(.+?\.jpg)">'imagelist = re.compile(pat2).findall(result1) #在result1內查詢所以的圖片鏈接x = 1for imageurl in imagelist: #這里我沒有用with open、是我手動創建的文件夾。imagename = "D:/work/日常任務2/img1/"+str(page)+str(x)+".jpg"imageurl = "http://"+imageurltry:urllib.request.urlretrieve(imageurl,filename=imagename)except urllib.error.URLError as e:if hasattr(e,"code"):x+=1if hasattr(e,"reason"):x+=1x+=1
if __name__ == '__main__':for i in range(1,30): #30代表獲取的范圍url= "http://list.jd.com/list.html?cat=9987,653,655&page="+str(i)craw(url,i)
第一個實戰也圓滿結束了,我們獲取了第一頁到29頁的所有目標圖片,并且將圖片存儲到本地目錄img1下。圖片的名字為:頁號+順序號.jpg 通過這個項目的學習,相信大家已經對如何手寫爬蟲代碼有了一定的思路,可以嘗試下自己手寫一個網絡爬蟲項目了。
鏈接爬蟲實戰:
所謂的 鏈接爬蟲 就是說 我們想把一個網頁中所有得鏈接地址提取出來,那么我們就需要來用鏈接爬蟲來實現。 本項目鏈接爬蟲實現的思路如下: 1、確定要爬取的入口鏈接。 2、根據需求構建好鏈接提取的正則表達式。 3、模擬成瀏覽器并爬取對應網頁。 4、根據2中的正則表達式提取出該網頁中包含的鏈接。 5、過濾掉重復的鏈接。 6、后續操作,保存寫入打印等等
下面我們模擬一下獲取blog.csdb.net網頁上的所有鏈接。
import re
import urllib.request
def getlink(url):headers = ("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) ""AppleWebKit/537.36 (KHTML, like Gecko)"" Chrome/39.0.2171.71 Safari/537.36")opener = urllib.request.build_opener() #模擬成瀏覽器opener.addheaders = [headers]urllib.request.install_opener(opener) #將opener設置為全局globalfile = urllib.request.urlopen(url) #獲取頁面信息data = str(file.read()) #轉換格式pat = '(https?://[^\s)";]+\.(\w|/)*)' #根據需求構建好鏈接表達式link = re.compile(pat).findall(data) #匹配我們需要的信息link = list(set(link)) #通過轉換去除重復元素return link
url = "http://blog.csdn.net/" #將爬取的地址賦值給url
linklist = getlink(url) #獲取對方網頁中包含的鏈接地址for link in linklist: #遍歷我們需要的鏈接print(link[0])
可看到打印結果:
鏈接爬取項目完成。
嗅事百科爬蟲實戰:
項目目標:爬取嗅事百科上的段子。 (www.qiushibaike.com)
本項目實現思路: 1、如同爬取京東圖片一樣,分析頁面規律,構造出網址變量。通過循環進行爬取。 2、構建一個自定義函數,來實現爬取某個網頁上的段子。 3、通過循環分別獲取多頁的各業URL鏈接,每頁分別調用一次getcontent(url.page)函數。
長話短說、看了這么久直接上代碼吧。 分別爬取了用戶及其對應的內容。代碼不難,加油
import urllib.request
import re
def getcontent(url,page):#老套路 先把自己模擬成瀏覽器 可百度搜索UA池headers = ("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64)"" AppleWebKit/537.36 (KHTML, like Gecko)"" Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36")opener = urllib.request.build_opener() #構造opener.addheaders = [headers]urllib.request.install_opener(opener) #設置opener為全局data = urllib.request.urlopen(url).read().decode('utf-8')#構建對應用戶提取的正則表達式userpat ='<div class="author clearfix">.*?<h2>(.*?)</h2>'#構建內容提取的正則表達式contentpat = '<div.*?span>(.*?)</span>'#尋找所有的用戶userlist = re.compile(userpat,re.S).findall(data) #re.S在匹配時為點任意匹配模式#所有內容contentlist = re.compile(contentpat,re.S).findall(data)# print(data)x=1for content in contentlist:content = content.replace("\n","")name = "content"+str(x)exec(name+'=content')x+=1y=1for user in userlist:name = "content"+str(y)print("用戶"+str(page)+str(y)+"是:"+user)print("內容是:")exec("print("+name+")")print("\n")y+=1
for i in range(1,3): #rang(1,3)為爬取頁面范圍url = "http://www.qiushibaike.com/hot/page/"+str(i)getcontent(url,i)
運行結果如下:
多線程爬蟲:
首先,什么是多線程爬蟲。 之前我們寫的爬蟲,程序在執行起來是有先后順序的,這種執行解構可以稱為單線程結構,對應的爬蟲稱為單線程爬蟲。如下圖所示
而多線程爬蟲,指的是爬蟲中的某部分程序可以并行執行,既在多條線上執行,這種執行結構稱為多線程爬蟲,對應的爬蟲稱為多線程爬蟲。如下圖
多線程爬蟲實戰:
要在python中使用多線程,我們可以導入threading模塊使用多線程功能。我們可以定義一個類并繼承threading.Thread類,將該類定義成一個線程。 在該類中,可以使用__init__(self)方法對線程進行初始化,在run(self)方法中寫上該線程要執行的程序。我們可以聲明多個這樣的類來構建多個線程并通過對應線程對象的start()方法啟動對應的線程。
寫一個簡單的多線程功能:
import threading
class A(threading.Thread):def __init__(self):threading.Thread.__init__(self)def run(self):for i in range(10):print("我是線程A")
class B(threading.Thread):def __init__(self):threading.Thread.__init__(self)def run(self):for i in range(10):print("我是線程B")
t1=A()
t1.start()
t2=B()
t2.start()
可以看出,A與B是并行執行的。
下面是多線程爬取糗事百科段子的代碼: 看不太明白也沒關系,我們后續也會進行更細致的講解。
# 使用了線程庫
import threading
# 隊列
from queue import Queue
# 解析庫
from lxml import etree
# 請求處理
import requests
# json處理
import json
import timeclass ThreadCrawl(threading.Thread):def __init__(self, threadName, pageQueue, dataQueue):#threading.Thread.__init__(self)# 調用父類初始化方法super(ThreadCrawl, self).__init__()# 線程名self.threadName = threadName# 頁碼隊列self.pageQueue = pageQueue# 數據隊列self.dataQueue = dataQueue# 請求報頭self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'}def run(self):print("啟動 " + self.threadName)while not CRAWL_EXIT:try:# 取出一個數字,先進先出# 可選參數block,默認值為True#1. 如果對列為空,block為True的話,不會結束,會進入阻塞狀態,直到隊列有新的數據#2. 如果隊列為空,block為False的話,就彈出一個Queue.empty()異常,page = self.pageQueue.get(False)url = "http://www.qiushibaike.com/8hr/page/" + str(page) +"/"#print urlcontent = requests.get(url, headers = self.headers).texttime.sleep(1)self.dataQueue.put(content)#print len(content)except:passprint("結束 " + self.threadName)class ThreadParse(threading.Thread):def __init__(self, threadName, dataQueue, filename, lock):super(ThreadParse, self).__init__()# 線程名self.threadName = threadName# 數據隊列self.dataQueue = dataQueue# 保存解析后數據的文件名self.filename = filename# 鎖self.lock = lockdef run(self):print("啟動" + self.threadName)while not PARSE_EXIT:try:html = self.dataQueue.get(False)self.parse(html)except:passprint("退出" + self.threadName)def parse(self, html):# 解析為HTML DOMhtml = etree.HTML(html)node_list = html.xpath('//div[contains(@id, "qiushi_tag")]')for node in node_list:# xpath返回的列表,這個列表就這一個參數,用索引方式取出來,用戶名username = node.xpath('./div/a/@title')[0]# 圖片連接image = node.xpath('.//div[@class="thumb"]//@src')#[0]# 取出標簽下的內容,段子內容content = node.xpath('.//div[@class="content"]/span')[0].text# 取出標簽里包含的內容,點贊zan = node.xpath('.//i')[0].text# 評論comments = node.xpath('.//i')[1].textitems = {"username" : username,"image" : image,"content" : content,"zan" : zan,"comments" : comments}# with 后面有兩個必須執行的操作:__enter__ 和 _exit__# 不管里面的操作結果如何,都會執行打開、關閉# 打開鎖、處理內容、釋放鎖with self.lock:# 寫入存儲的解析后的數據self.filename.write(json.dumps(items, ensure_ascii = False).encode("utf-8") + "\n")CRAWL_EXIT = False
PARSE_EXIT = Falsedef main():# 頁碼的隊列,表示20個頁面pageQueue = Queue(20)# 放入1~10的數字,先進先出for i in range(1, 21):pageQueue.put(i)# 采集結果(每頁的HTML源碼)的數據隊列,參數為空表示不限制dataQueue = Queue()filename = open("duanzi.json", "a")# 創建鎖lock = threading.Lock()# 三個采集線程的名字crawlList = ["采集線程1號", "采集線程2號", "采集線程3號"]# 存儲三個采集線程的列表集合threadcrawl = []for threadName in crawlList:thread = ThreadCrawl(threadName, pageQueue, dataQueue)thread.start()threadcrawl.append(thread)# 三個解析線程的名字parseList = ["解析線程1號","解析線程2號","解析線程3號"]# 存儲三個解析線程threadparse = []for threadName in parseList:thread = ThreadParse(threadName, dataQueue, filename, lock)thread.start()threadparse.append(thread)# 等待pageQueue隊列為空,也就是等待之前的操作執行完畢while not pageQueue.empty():pass# 如果pageQueue為空,采集線程退出循環global CRAWL_EXITCRAWL_EXIT = Trueprint("pageQueue為空")for thread in threadcrawl:thread.join()print("1")while not dataQueue.empty():passglobal PARSE_EXITPARSE_EXIT = Truefor thread in threadparse:thread.join()print("2")with lock:# 關閉文件filename.close()print("謝謝使用!")if __name__ == "__main__":main()
總結:
本篇文章是基于前面3篇基礎而做出來的實戰項目。內容由易到難。 只要多回顧之前的知識,掌握好爬蟲的思維,就能順利寫出來我們所需要的爬蟲代碼。 后續文章會繼續講解,請關注博客更新。 下一篇:初識Python爬蟲框架Scrapy
總結
以上是生活随笔 為你收集整理的004:Python爬虫实战 由易到难(图文解析) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。