Python中如何通过robots.txt的sitemap抓取网站当天新产生的URL

有的网站有 sitemap 文件,本就是给百度这类爬虫看的,会把当天新产生的 URL 和最近更新的 URL 放在 sitemap 文件里,这样就不用整过遍历网站,节约单宽,尤其是大型网站有上千万,过亿页面这类网站,通过抓取 sitemap 来发现新增 URL 比较有效。 就像这篇文章说的一样: https://www.yuanrenxue.com/crawler/crawler-tricks-3.html


Python中如何通过robots.txt的sitemap抓取网站当天新产生的URL

2 回复

要抓取robots.txt里sitemap中的当天新URL,可以这样搞:

import requests
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
from urllib.robotparser import RobotFileParser
from urllib.parse import urlparse

def get_today_urls_from_sitemap(target_url):
    """从目标网站的sitemap中提取当天生成的URL"""
    
    # 解析robots.txt获取sitemap
    parsed_url = urlparse(target_url)
    base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
    robots_url = f"{base_url}/robots.txt"
    
    # 获取robots.txt内容
    try:
        response = requests.get(robots_url, timeout=10)
        response.raise_for_status()
    except requests.RequestException as e:
        print(f"获取robots.txt失败: {e}")
        return []
    
    # 解析sitemap地址
    sitemap_urls = []
    for line in response.text.split('\n'):
        line = line.strip()
        if line.lower().startswith('sitemap:'):
            sitemap_url = line.split(':', 1)[1].strip()
            sitemap_urls.append(sitemap_url)
    
    if not sitemap_urls:
        print("未在robots.txt中找到sitemap")
        return []
    
    today_urls = []
    today = datetime.now(timezone.utc).date()
    
    # 处理每个sitemap
    for sitemap_url in sitemap_urls:
        try:
            sitemap_resp = requests.get(sitemap_url, timeout=10)
            sitemap_resp.raise_for_status()
            
            # 解析XML
            root = ET.fromstring(sitemap_resp.content)
            
            # 命名空间处理
            namespaces = {'ns': 'http://www.sitemaps.org/schemas/sitemap/0.9'}
            
            # 查找所有URL条目
            for url_elem in root.findall('.//ns:url', namespaces):
                loc_elem = url_elem.find('ns:loc', namespaces)
                lastmod_elem = url_elem.find('ns:lastmod', namespaces)
                
                if loc_elem is not None:
                    url = loc_elem.text.strip()
                    
                    # 检查最后修改时间
                    if lastmod_elem is not None and lastmod_elem.text:
                        try:
                            # 解析时间戳
                            lastmod_str = lastmod_elem.text.strip()
                            if 'T' in lastmod_str:
                                lastmod_dt = datetime.fromisoformat(lastmod_str.replace('Z', '+00:00'))
                            else:
                                lastmod_dt = datetime.strptime(lastmod_str, '%Y-%m-%d')
                            
                            # 转换为UTC日期
                            if lastmod_dt.tzinfo is None:
                                lastmod_dt = lastmod_dt.replace(tzinfo=timezone.utc)
                            else:
                                lastmod_dt = lastmod_dt.astimezone(timezone.utc)
                            
                            # 检查是否是今天
                            if lastmod_dt.date() == today:
                                today_urls.append(url)
                                
                        except (ValueError, AttributeError) as e:
                            print(f"解析时间失败 {url}: {e}")
                            continue
                            
        except requests.RequestException as e:
            print(f"获取sitemap失败 {sitemap_url}: {e}")
            continue
        except ET.ParseError as e:
            print(f"解析XML失败 {sitemap_url}: {e}")
            continue
    
    return today_urls

# 使用示例
if __name__ == "__main__":
    target_site = "https://example.com"  # 替换为目标网站
    
    today_urls = get_today_urls_from_sitemap(target_site)
    
    print(f"找到 {len(today_urls)} 个当天URL:")
    for url in today_urls:
        print(f"  - {url}")

关键点:

  1. 先抓robots.txt找sitemap地址
  2. 解析sitemap XML,注意命名空间
  3. 用lastmod字段判断是否当天更新
  4. 处理时区问题,统一转UTC比较

注意:不是所有网站都提供lastmod字段,有些可能更新不及时。

简单说就是解析sitemap的lastmod时间过滤出当天URL。


这算不算是个骚操作?

回到顶部