天天看點

用lxml跑一個簡單的流程 3.0版本

多程序抓取頁面

  1. 導入第三方thread包,定義一個程序池
  2. 将抓取的一級頁面放進程序池裡
  3. 啟動程序池,自定義用幾個程序來執行函數
  4. 在程序池中引入函數,定義download函數,執行二級抓取資料
  5. 退出線程
# import 先導入内置的庫,再導入第三方庫
import time
import threading
from queue import Queue
import lxml.etree
import requests

START_URL= 'http://qianmu.iguye.com/2018USNEWS世界大學排名'
# 啟動的線程數量
DOWNLOAD_LINE = 
# 定義Queue,隊列,儲存等抓取的url
link_queue = Queue()
# 線程清單,儲存Thread對象
threads = []
download_pages = 


def fetch(url, raise_err=True):
    global download_pages
    try:
        # 使用requests抓取url
        r = requests.get(url)
        r.encoding = 'utf-8'
        # 每讀取一個,加1,顯示已經讀取的資料
        download_pages += 
        return r.text
    except Exception as e:
        # 如果報錯則print出來
        print(e)
    else:
        # 如果沒報錯,則檢查http的傳回碼是否正常
        if raise_err:
            r.raise_for_status()


def parse_university(html):
    selector = lxml.etree.HTML(html)
    title = selector.xpath('//*[@id="wikiContent"]/h1/text()')[]
    # 列印大學名
    print(title)

    infobox = selector.xpath('//div[@class="infobox"]')[]
    keys = infobox.xpath('./table//tr/td[1]/p//text()')
    cols = infobox.xpath('./table//tr/td[2]')
    values = [''.join(col.xpath('.//text()')) for col in cols]

    # 用清單的形式循環出keys, values
    for k, v in zip(keys, values):
        print('%s:%s'%(k, v))
    print('-' * )


# 線程執行這個函數,不退出,一直到link為空
def download():
    while True:
        # get阻塞方法
        link = link_queue.get()
        if link is None:
            break
        # 執行下載下傳、解析操作
        parse_university(fetch(link))
        # 向隊列發送完成任務的消息
        link_queue.task_done()
        print('remaining queue: %s' % link_queue.qsize())


if __name__ == '__main__':
    start_time = time.time()
    selector = lxml.etree.HTML(fetch(START_URL))
    links = selector.xpath('//*[@id="content"]/table/tbody/tr/td/a/@href')

    # 第一步,抓取第一個頁面放進Q裡面
    for link in links:
        # 加判斷,如果連結有錯,重新給附個連結
        if not link.startswith('http://'):
            link = 'http://qianmu.iguye.com/%s' % link
        link_queue.put(link)

    # 第二步,啟動線程
    for i in range(DOWNLOAD_LINE):
        t = threading.Thread(target=download)
        t.start()
        # 把讀入的内容
        threads.append(t)
    # 堵塞,前面的都執行完了之後,再執行他,然後退出程式
    link_queue.join()

    # 向隊列發送DOWNLOADER_NUM個None,以通知線程退出
    for i in range(DOWNLOAD_LINE):
        link_queue.put(None)

    # 退出線程
    for t in threads:
        t.join()
    # 計算抓取的耗時
    cost_seconds = time.time() - start_time
    print('download %s pages in %.2f seconds'%(download_pages, cost_seconds))
           

繼續閱讀