zoukankan      html  css  js  c++  java
  • 抓取一个网站全部的网页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 = '
    '.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('
    ')
            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('
    ')
            #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文件。

    转载请注明出处
  • 相关阅读:
    zoj 2316 Matrix Multiplication 解题报告
    BestCoder7 1001 Little Pony and Permutation(hdu 4985) 解题报告
    codeforces 463C. Gargari and Bishops 解题报告
    codeforces 463B Caisa and Pylons 解题报告
    codeforces 463A Caisa and Sugar 解题报告
    CSS3新的字体尺寸单位rem
    CSS中文字体对照表
    引用外部CSS的link和import方式的分析与比较
    CSS样式表引用方式
    10个CSS简写/优化技巧
  • 原文地址:https://www.cnblogs.com/lnlin/p/14840078.html
Copyright © 2011-2022 走看看