【问题标题】:How to obtain all the links in a domain using Python?如何使用 Python 获取域中的所有链接?
【发布时间】:2016-04-10 10:39:12
【问题描述】:

我想使用 Python 获取给定“根”URL(在列表中)的域中的所有链接。假设给定一个 URL http://www.example.com 这应该返回与根 URL 相同域的此页面上的所有链接,然后递归访问这些链接中的每一个并提取相同域的所有链接等等。我所说的相同域的意思是,如果给定http://www.example.com,我想要返回的唯一链接是http://www.example.com/somethinghttp://www.example.com/somethingelse ...任何外部的东西,例如http://www.otherwebsite.com,都应该被丢弃。如何使用 Python 做到这一点?

编辑:我尝试使用 lxml。我不认为这完全有效,我不确定如何考虑到已处理页面的链接(导致无限循环)。

import urllib
import lxml.html

#given a url returns list of all sublinks within the same domain
def getLinks(url):
        urlList = []
        urlList.append(url)
        sublinks = getSubLinks(url)
        for link in sublinks:
                absolute = url+'/'+link
                urlList.extend(getLinks(absolute))
         return urlList

#determine whether two links are within the same domain
def sameDomain(url, dom):
    return url.startswith(dom)

#get tree of sublinks in same domain, url is root
def getSubLinks(url):
    sublinks = []
    connection = urllib.urlopen(url)
    dom = lxml.html.fromstring(connection.read())
    for link in dom.xpath('//a/@href'):
                if not (link.startswith('#') or link.startswith('http') or link.startswith('mailto:')):
                        sublinks.append(link)
    return sublinks

~

【问题讨论】:

  • 从问题标签看来您已经知道该使用什么了。也许你可以展示你尝试过的东西,否则我认为这个问题太笼统了。有一些网络抓取框架,例如 scrapy,可能会对您有所帮助。
  • 这个问题比较难,页面中的一些链接没有协议前缀,提供本地路径。 “..”是一个有效的 URL。你想关注什么而不是关注什么?
  • 我只想关注以根 URL 为前缀的任何内容。但是,一些相对链接没有以根 URL 为前缀,但如果我将根 URL 附加到它们之前,它们将是有效的。我也想要这些。

标签: python web-scraping beautifulsoup lxml


【解决方案1】:

@namita 的代码中存在一些错误。我对其进行了修改,现在效果很好。

import sys
import requests
import hashlib
from bs4 import BeautifulSoup
from datetime import datetime


def get_soup(link):
    """
    Return the BeautifulSoup object for input link
    """
    request_object = requests.get(link, auth=('user', 'pass'))
    soup = BeautifulSoup(request_object.content, "lxml")
    return soup

def get_status_code(link):
    """
    Return the error code for any url
    param: link
    """
    try:
        error_code = requests.get(link).status_code
    except requests.exceptions.ConnectionError:
        error_code = -1
    return error_code

def find_internal_urls(main_url, depth=0, max_depth=2):
    all_urls_info = []

    soup = get_soup(main_url)
    a_tags = soup.findAll("a", href=True)

    if main_url.endswith("/"):
        domain = main_url
    else:
        domain = "/".join(main_url.split("/")[:-1])
    print(domain)
    if depth > max_depth:
        return {}
    else:
        for a_tag in a_tags:
            if "http://" not in a_tag["href"] and "https://" not in a_tag["href"] and "/" in a_tag["href"]:
                url = domain + a_tag['href']
            elif "http://" in a_tag["href"] or "https://" in a_tag["href"]:
                url = a_tag["href"]
            else:
                continue
            # print(url)

            status_dict = {}
            status_dict["url"] = url
            status_dict["status_code"] = get_status_code(url)
            status_dict["timestamp"] = datetime.now()
            status_dict["depth"] = depth + 1
            all_urls_info.append(status_dict)
    return all_urls_info


if __name__ == "__main__":
    url = # your domain here
    depth = 1
    all_page_urls = find_internal_urls(url, 0, 2)
    # print("\n\n",all_page_urls)
    if depth > 1:
        for status_dict in all_page_urls:
            find_internal_urls(status_dict['url'])

【讨论】:

    【解决方案2】:

    这就是我所做的,只关注像http://domain[xxx] 这样的完整网址。很快,但有点脏。

    import requests
    import re
    
    domain = u"stackoverflow.com"
    http_re = re.compile(u"(http:\/\/" + domain + "[\/\w \.-]*\/?)")
    
    visited = set([])
    def visit (url):
        visited.add (url)
        extracted_body = requests.get (url).text
        matches = re.findall (http_re, extracted_body)
        for match in matches:
            if match not in visited :
                visit (match)
    
    visit(u"http://" + domain)    
    print (visited)
    

    【讨论】:

      【解决方案3】:
      import sys
      import requests
      import hashlib
      from bs4 import BeautifulSoup
      from datetime import datetime
      
      def get_soup(link):
          """
          Return the BeautifulSoup object for input link
          """
          request_object = requests.get(link, auth=('user', 'pass'))
          soup = BeautifulSoup(request_object.content)
          return soup
      
      def get_status_code(link):
          """
          Return the error code for any url
          param: link
          """
          try:
              error_code = requests.get(link).status_code
          except requests.exceptions.ConnectionError:
              error_code = 
          return error_code
      
      def find_internal_urls(lufthansa_url, depth=0, max_depth=2):
          all_urls_info = []
          status_dict = {}
          soup = get_soup(lufthansa_url)
          a_tags = soup.findAll("a", href=True)
      
          if depth > max_depth:
              return {}
          else:
              for a_tag in a_tags:
                  if "http" not in a_tag["href"] and "/" in a_tag["href"]:
                      url = "http://www.lufthansa.com" + a_tag['href']
                  elif "http" in a_tag["href"]:
                      url = a_tag["href"]
                  else:
                      continue
                  status_dict["url"] = url
                  status_dict["status_code"] = get_status_code(url)
                  status_dict["timestamp"] = datetime.now()
                  status_dict["depth"] = depth + 1
                  all_urls_info.append(status_dict)
          return all_urls_info
      if __name__ == "__main__":
          depth = 2 # suppose 
          all_page_urls = find_internal_urls("someurl", 2, 2)
          if depth > 1:
              for status_dict in all_page_urls:
                  find_internal_urls(status_dict['url'])
      

      上面的 sn-p 包含了从 lufthansa arlines 网站上抓取 url 的必要模块。这里唯一额外的是您可以指定要递归抓取的深度。

      【讨论】:

      • 这是有道理的,但这是如何递归的呢?似乎只能找到第一个“级别”的链接。
      • 你添加深度,它会搜索到那个深度。
      • 但是 find_internal_urls 实际上在哪里被自己调用并因此在链接上递归?
      • 我提供了模块,您可以根据需要在任何地方使用!
      • 我认为这行不通。您正在改变 find_internal_urls 中的 all_page_urls。因此,在 main 方法中,您正在更改循环中迭代的内容,python 将其视为聚会犯规。
      【解决方案4】:

      您可以使用正则表达式过滤掉此类链接

      例如

      <a\shref\=\"(http\:\/\/example\.com[^\"]*)\"
      

      以上面的正则表达式为参考,并以此为基础开始编写脚本。

      【讨论】:

        【解决方案5】:

        根据您的问题标签,我假设您使用的是 Beautiful Soup。 首先,您显然需要下载网页,例如使用 urllib.request。在你这样做并将内容放在一个字符串中之后,你将它传递给 Beautiful Soup。在那之后,你可以找到所有带有soup.find_all('a') 的链接,假设soup 是你漂亮的soup 对象。之后,您只需检查href:

        最简单的版本是只检查“http://www.example.com”是否在 href 中,但这不会捕获相关链接。我想一些狂野的正则表达式会做(查找所有以“www.example.com”或以“/”或以“?”(php)开头的内容),或者您可能会查找包含 www 的所有内容,但不是www.example.com 并丢弃它等。正确的策略可能取决于您正在抓取的网站,以及它的编码风格。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-19
          • 2016-04-18
          • 1970-01-01
          • 2016-06-12
          • 1970-01-01
          • 2012-02-04
          相关资源
          最近更新 更多