抓取一個網站全部的網頁URL--Python、爬蟲


要獲得一個網站所有的網頁URL,思路很簡單,就是一遍遍分析新得到的網頁中有哪些URL,然后不斷重復的。

下面以抓取CSDN為例:

首先是一些輔助用的函數:

 1 def getResponse(url):# 使用requests獲取Response
 2     headers = {
 3         'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36'
 4     }
 5     response = requests.get(url=url, headers=headers)
 6     return response
 7 
 8 def getHTMLBySelenium(url):# 使用selenium獲取頁面的page_text
 9     try:
10         chrome_options =Options()
11         chrome_options.add_argument('--headless')
12         browser = webdriver.Chrome(executable_path='C:/Users/Administrator/Desktop/chromedriver_win32/chromedriver.exe', options=chrome_options)
13         browser.get(url)
14         time.sleep(2)
15         page_text = browser.page_source
16         browser.quit()
17         return page_text
18     except Exception as e:
19         return ''
20     
21 def getBlog(url):# 獲取頁面內容  
22     try:
23         page_text = getHTMLBySelenium(url)
24         tree = etree.HTML(page_text)
25         allText = tree.xpath('//body//text()')
26         text = '\n'.join(allText)
27         title = url.replace('/', '_')
28         title = title.replace('.', '_')
29         title = title.replace(':', '_')
30         with open('全站/' + title + '.txt', 'w', encoding='utf-8') as fp:
31             fp.write(text)
32     except Exception as e:
33         return

提取一個頁面中包含的所有其他頁面的URL,具體網站具體分析,這里是CSDN的獲取方式:

def getLinks(url):
    try:
        page_text = getHTMLBySelenium(url)
        tree = etree.HTML(page_text)
        all_href = tree.xpath('//a')
        links = []

        for href in all_href:
            link = href.xpath('./@href')
            if len(link) == 0:
                continue
            link = link[0]
            if link.startswith('https://blog.csdn.net'):
                links.append(link)
        return links
    except Exception as e:
        return []

下面就是遞歸獲取頁面URL的過程,先看一段簡單的代碼:

urls = set()# 存儲已經被操作過的URL
temp1 = set()# 存儲正在被操作的URL
temp2 = set()# 存儲新獲取的URL

temp1.add('url')# 程序最開始的分析的頁面,可以是網站首頁URL

while temp1:# temp1不為空則程序一直運行
    for url in temp1:
        if url in urls:# url在urls 代表這條url已經被處理
            continue
        doSomeThing(url)# 處理url
        for link in getLinks(url):# 分析url表示的頁面中有哪些其他的URL
            if link in urls:
                continue
            if link in temp2:
                continue
            temp2.add(link)
    # temp1中url處理完畢
    # 將temp2內容賦給temp1,並清空temp2
    temp1 = temp2.copy()
    temp2.clear()

從上述代碼可以看到整個程序的運行邏輯,但在具體使用時有一些需要注意的問題:

首先是我們用什么保存獲取的鏈接,我最開始使用的是 set 並將urls,temp1,temp2分別用一個文本文件做備

份,因為我不知道程序會在那個節點出問題,用文本保存后可以避免程序出問題后要重頭開始運行代碼,這也

是上面的輔助函數我都用 try ... except... 的原因。

按照上述思路我完成了第一版的代碼,set + 文本文件,然后程序在周末跑了兩天之后,我周一發現程序把電

腦內存跑滿了(win10 + 16G內存)電腦卡死了,然后強制關機重啟之后我看了一下存儲URL的文件,程序最

外層循環大概運行到第四次,temp2中有幾十萬條URL。

既然內存不夠,接下來我就想將URL存儲到數據庫中,然后我選擇用MySQL代替set存儲URL,仍然用文本做

備份。

下面是這一版本的代碼,如果運行兩天之后程序沒出現內存的問題,這個隨筆就不會再更新:

# ---- 用pymysql 操作數據庫
def get_connection():
    conn = pymysql.connect(host=host, port=port, db=db, user=user, password=password)
    return conn

#打開數據庫連接
conn = get_connection()

cnt = 1
loop = 2
cursor = conn.cursor()
cursor1 = conn.cursor()
cursor2 = conn.cursor()

while True:
    print(f'Loop {loop}')
    loop += 1
    # 遍歷temp1
    cursor1.execute("select * from csdn_temp1")
    while True:
        temp1Res = cursor1.fetchone()
        # temp1 遍歷完成
        if temp1Res is None:
            #表示已經取完結果集
            break
        print (temp1Res)
        url = temp1Res[0]
        url = re.sub('[\u4e00-\u9fa5]', '', url)
        cursor.execute("select * from csdn_urls where url = %s", [url])
        urlsRes = cursor.fetchone()
        # 已經抓過這一條鏈接 continue
        if urlsRes is not None:
            continue
        #if cnt % 100 == 0:
            #print(url)
        cnt += 1
        sql = "insert ignore into csdn_urls values(%s)"        
        cursor.execute(sql,(url))
        conn.commit()
        with open('urls.txt', 'a', encoding='utf-8') as fp:
            fp.write(url)
            fp.write('\n')
        getBlog(url)
        links = getLinks(url)
        #toTemp2Urls = []
        for link in links:
            # 已經抓過這一條鏈接 或者 temp2 已經有了這一鏈接 continue
            cursor.execute("select * from csdn_urls where url = %s", [link])
            urlsRes = cursor.fetchone()
            if urlsRes is not None:
                continue
            cursor2.execute("select * from csdn_temp2 where url = %s", [link])
            temp2Res = cursor2.fetchone()
            if temp2Res is not None:
                continue
            #toTemp2Urls.append(link)
            sql = "insert ignore into csdn_temp2 values(%s)"
            link = re.sub('[\u4e00-\u9fa5]', '', link)
            cursor2.execute(sql,(link))
            conn.commit()
            with open('temp2.txt', 'a', encoding='utf-8') as fp:
                fp.write(link)
                fp.write('\n')
        #sql="insert ignore into csdn_temp2 values(%s)"
        #cursor2.executemany(sql,toTemp2Urls)
        conn.commit()
        #toTemp2Urls = []        
    conn.commit()
    cursor.execute("rename table csdn_temp1 to csdn_temp")
    conn.commit()
    cursor.execute("rename table csdn_temp2 to csdn_temp1")
    conn.commit()
    cursor.execute("rename table csdn_temp to csdn_temp2")
    conn.commit()
    # 刪除temp2數據
    cursor.execute("delete from csdn_temp2")
    conn.commit()
    os.rename('temp1.txt', 'temp3.txt')
    os.rename('temp2.txt', 'temp1.txt')
    os.rename('temp3.txt', 'temp2.txt')
    with open('temp2.txt', 'w', encoding='utf-8') as fp:
        fp.write('')

在寫上述代碼時,我遇到了一個問題,表改名后沒有及時commit,使得我之前第一版抓的幾十萬條URL清

空了,而且備份用的文本文件也清空了。修改完之后得到了上述代碼。

整個代碼的調試過程以及寫代碼時的思路可以在我的GitHub上看jupyter文件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM