示例圖片
1.0版本
import requests
from lxml import etree
max_requests = 5
requests_count = 0
# 抓取單個大學鏈接,如http://qianmu.iguye.com/哈佛大學
r = requests.get("http://qianmu.iguye.com/2018USNEWS世界大學排名")
dom = etree.HTML(r.text)
links = dom.xpath("//*[@id='content']/table/tbody/tr/td[2]/a/@href")
for link in links:
requests_count += 1
if requests_count > max_requests:
break
if not link.startswith("http://"):
link = 'http://www.qianmu.org/%s'%link
print(link)
# 抓取表格內容
r = requests.get(link)
dom = etree.HTML(r.text.replace("\t",""))
# 抓取第一列的內容
keys = dom.xpath("//div[@id='wikiContent']/div[@class='infobox']/table/tbody/tr/td[1]//text()")
# print(keys)
# 抓取第二列的內容
cols = dom.xpath("//div[@id='wikiContent']/div[@class='infobox']/table/tbody/tr/td[2]")
values = [",".join(col.xpath('.//text()')) for col in cols]
# print(values)
# info = {}
# for i in range(len(keys)):
# info[keys[i]] = values[i]
info = dict(zip(keys, values))
print(info)
2.0版本,封裝函數
import requests
from lxml import etree
# 入口頁面地址
start_url = "http://qianmu.iguye.com/2018USNEWS世界大學排名"
# 存放待抓取頁面地址
link_queue = []
def fetch(url):
"""執行網頁的抓取"""
r = requests.get(url)
# 返回抓取的頁面內容並取出頁面中的製表符
return r.text.replace("\t","")
def parse(html):
"""解析入口頁面"""
dom = etree.HTML(html)
# 獲取頁面中的表格每一行的第二列中的超鏈接
links = dom.xpath("//div[@id='content']/table/tbody/tr/td[2]/a/@href")
# 將這些鏈接放入待抓取隊列中
link_queue.extend(links)
def parse_university(html):
"""解析大學詳情頁面"""
dom = etree.HTML(html)
# 選擇出表格的父節點,以減少重複代碼
infobox = dom.xpath("//div[@id='wikiContent']/div[@class='infobox']")[0]
# 選擇出表格中每一行的第一列中的文本
keys = infobox.xpath("./table/tbody/tr/td[1]//text()")
# 選擇出表格中每一行的第二列節點
cols = infobox.xpath(".//table/tbody/tr/td[2]")
# 便利第二列的節點,病提取出每一個單元格中的文本
values = [",".join(col.xpath(".//text()")) for col in cols]
# info = {}
# for i in range(len(keys)):
# info[keys[i]] = values[i]
# print(info)
# 講第一列、第二列中的數據合併成一個字典,組成大學信息
info = dict(zip(keys, values))
print(info)
if __name__ == "__main__":
# 抓取並處理入口頁面,提取首頁內的大學頁面鏈接
parse(fetch(start_url))
# 最大請求數
max_requests = 5
# 請求計數器
requests_count = 0
# 反轉隊列,以便先進先出
link_queue.reverse()
while link_queue:
# 獲取最前面的一個鏈接
link = link_queue.pop()
requests_count += 1
if requests_count > max_requests:
break
# 如果有不規則的鏈接,則補全
if not link.startswith("http://"):
link = "http://qianmu.iguye.com/%s" % link
# 抓取並處理大學詳情頁面
print(link)
parse_university(fetch(link))
# print("%s links"% len(links))
3.0版本,多線程
import threading
from queue import Queue
import requests
import time
from lxml import etree
# 入口頁面地址
start_url = "http://qianmu.iguye.com/2018USNEWS世界大學排名"
# 存放待抓取頁面地址
link_queue = Queue()
# 下載器線程數量
DOWNLOADER_NUM = 10
# 線程池
threads = []
# 統計下載鏈接的數量
download_pages = 0
def fetch(url):
"""執行網頁的抓取"""
r = requests.get(url)
global download_pages
download_pages += 1
# 返回抓取的頁面內容並取出頁面中的製表符
return r.text.replace("\t","")
def parse(html):
"""解析入口頁面"""
dom = etree.HTML(html)
# 獲取頁面中的表格每一行的第二列中的超鏈接
links = dom.xpath("//div[@id='content']/table/tbody/tr/td[2]/a/@href")
# 將這些鏈接放入待抓取隊列中
for link in links:
# 如果有不規則的鏈接,則補全
if not link.startswith("http://"):
link = "http://qianmu.iguye.com/%s" % link
# 將鏈接放入下載隊列
link_queue.put(link)
def parse_university(html):
"""解析大學詳情頁面"""
dom = etree.HTML(html)
# 選擇出表格的父節點,以減少重複代碼
infobox = dom.xpath("//div[@id='wikiContent']/div[@class='infobox']")[0]
# 選擇出表格中每一行的第一列中的文本
keys = infobox.xpath("./table/tbody/tr/td[1]//text()")
# 選擇出表格中每一行的第二列節點
cols = infobox.xpath(".//table/tbody/tr/td[2]")
# 便利第二列的節點,病提取出每一個單元格中的文本
values = [",".join(col.xpath(".//text()")) for col in cols]
# 講第一列、第二列中的數據合併成一個字典,組成大學信息
info = dict(zip(keys, values))
print(info)
def downloader():
"""下載器,主要執行下載和解析操作"""
while True:
# 從隊列中讀取一個鏈接,如果沒有,則阻塞
link = link_queue.get()
# 如果收到的鏈接是None,則退出循環
if link is None:
break
# 下載並解析大學詳情網頁
print(link)
parse_university(fetch(link))
# 向隊列發送任務完成信號
link_queue.task_done()
print("remining queue: %s"%link_queue.qsize())
if __name__ == "__main__":
# 抓取並處理入口頁面,提取首頁內的大學頁面鏈接
start_time = time.time()
parse(fetch(start_url))
for i in range(DOWNLOADER_NUM):
t = threading.Thread(target=downloader)
t.start()
threads.append(t)
# 讓隊列一直阻塞到全部任務完成
link_queue.join()
# 向隊列中發送DOWNLOADER_NUM個None
for i in range(DOWNLOADER_NUM):
link_queue.put(None)
# 退出線程
for t in threads:
t.join()
print("download %s page in %.2f seconds"%(download_pages,time.time()-start_time))
4.0版本,分佈式爬蟲(多線程+多進程)
import signal
import threading
from queue import Queue
import redis
import requests
import time
from lxml import etree
# 入口頁面地址
start_url = "http://qianmu.iguye.com/2018USNEWS世界大學排名"
# 存放待抓取頁面地址
# link_queue = Queue()
# 下載器線程數量
DOWNLOADER_NUM = 10
# 下載延遲
DOWNLOADER_DELAY = 0.1
# 線程池
threads = []
# 統計下載鏈接的數量
download_pages = 0
r = redis.Redis()
thread_on = True
def fetch(url):
"""執行網頁的抓取"""
r = requests.get(url)
global download_pages
download_pages += 1
# 返回抓取的頁面內容並取出頁面中的製表符
return r.text.replace("\t","")
def parse(html):
"""解析入口頁面"""
dom = etree.HTML(html)
# 獲取頁面中的表格每一行的第二列中的超鏈接
links = dom.xpath("//div[@id='content']/table/tbody/tr/td[2]/a/@href")
# 將這些鏈接放入待抓取隊列中
for link in links:
# 如果有不規則的鏈接,則補全
if not link.startswith("http://"):
link = "http://qianmu.iguye.com/%s" % link
# 將鏈接放入下載隊列
# link_queue.put(link)
if r.sadd("qianmu.seen",link):
r.rpush("qianmu.queue",link)
def parse_university(html):
"""解析大學詳情頁面"""
dom = etree.HTML(html)
# 選擇出表格的父節點,以減少重複代碼
infobox = dom.xpath("//div[@id='wikiContent']/div[@class='infobox']")[0]
# 選擇出表格中每一行的第一列中的文本
keys = infobox.xpath("./table/tbody/tr/td[1]//text()")
# 選擇出表格中每一行的第二列節點
cols = infobox.xpath(".//table/tbody/tr/td[2]")
# 便利第二列的節點,病提取出每一個單元格中的文本
values = [",".join(col.xpath(".//text()")) for col in cols]
# 講第一列、第二列中的數據合併成一個字典,組成大學信息
info = dict(zip(keys, values))
# print(info)
r.lpush("qianmu.item",info)
def downloader(i):
"""下載器,主要執行下載和解析操作"""
while thread_on:
# 從隊列中讀取一個鏈接,如果沒有,則阻塞
link = r.lpop("qianmu.queue")
# 如果收到的鏈接是None,則退出循環
if link:
# 下載並解析大學詳情網頁
parse_university(fetch(link))
print("remining-%s queue: %s"%(i,r.llen("qianmu.queue")))
time.sleep(DOWNLOADER_DELAY)
print("Thread-%s exit now..." % i)
def signal_handler(signnum,frame):
print("received CRTL+C, wait for exit gracfully...")
global thread_on
thread_on = False
if __name__ == "__main__":
# 抓取並處理入口頁面,提取首頁內的大學頁面鏈接
start_time = time.time()
parse(fetch(start_url))
for i in range(DOWNLOADER_NUM):
t = threading.Thread(target=downloader,args=(i+1,))
t.start()
threads.append(t)
print("Thread(%s) started..." %t.name)
signal.signal(signal.SIGINT,signal_handler)
# 退出線程
for t in threads:
t.join()
# 計算程序執行消耗的時間
print("download %s page in %.2f seconds"%(download_pages,time.time()-start_time))