抓取大學排名

示例圖片


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))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章