在运行时生成python正则表达式以匹配从'n'到无限的数字

1 投票
4 回答
1090 浏览
提问于 2025-04-16 13:04

我正在使用scrapy这个工具来爬取一个网站并提取数据。scrapy会用一些基于正则表达式的规则来判断一个页面是否需要解析,或者一个链接是否需要跟进。

我正在为我的爬虫实现一个恢复功能,这样它就可以从上次访问的页面继续爬取。为此,我在启动爬虫时会从数据库中获取最后跟进的链接。

我的网站链接看起来像 http://foobar.com/page1.html,所以通常来说,跟进每个这样的链接的规则正则表达式会是 /page\d+\.html

但是,我该如何写一个正则表达式来匹配,比如说,第15页及之后的页面呢?另外,由于我事先不知道起始点,我该如何在运行时生成这个正则表达式呢?

4 个回答

2

稍微扩展一下Kabie的回答:

def genregex(n):
    nstr = str(n)
    same_digit = ''.join('[' + "0123456789"[int(d):] + ']' for d in nstr)
    return "\d{%d,}|%s" % (len(nstr) + 1, same_digit)

如果你的网站出现了前导零,修改代码来处理这个问题其实很简单。不过,这样做似乎不是最好的方法。

在scrapy中,你还有其他几种选择。你可能在使用SgmlLinkExtractor,在这种情况下,最简单的方法就是把你自己的函数作为process_value这个参数传进去,这样就可以进行自定义过滤了。

CrawlSpider可以进行很多自定义,但如果它不适合你的任务,你可以看看BaseSpider

3

为什么不把页码分组,然后检查它是否符合要求呢:

>>> m=re.match("/page(\d+)\.html","/page18.html")
>>> if m:
    ID=int(m.groups()[0])
>>> ID > 15
True

或者更具体地说,就是你所要求的:

>>> def genRegex(n):
    return ''.join('[' + "0123456789"[int(d):] + ']' for d in str(n))

>>> genRegex(123)
'[123456789][23456789][3456789]'
2

试试这个:

def digit_match_greater(n):
    digits = str(n)
    variations = []
    # Anything with more than len(digits) digits is a match:
    variations.append(r"\d{%d,}" % (len(digits)+1))
    # Now match numbers with len(digits) digits.
    # (Generate, e.g, for 15, "1[6-9]", "[2-9]\d")
    # 9s can be skipped -- e.g. for >19 we only need [2-9]\d.
    for i, d in enumerate(digits):
        if d != "9": 
            pattern = list(digits)
            pattern[i] = "[%d-9]" % (int(d) + 1)
            for j in range(i+1, len(digits)):
                pattern[j] = r"\d"
            variations.append("".join(pattern))
    return "(?:%s)" % "|".join("(?:%s)" % v for v in variations)

结果发现,匹配大于给定参数的数字要简单一些。所以如果你给它15,它会返回一个字符串,用来匹配16及更大的数字,具体来说...

(?:(?:\d{3,})|(?:[2-9]\d)|(?:1[6-9]))

然后你可以把这个替换到你的表达式里,代替\d+,像这样:

exp = re.compile(r"page%s\.html" % digit_match_greater(last_page_visited))

撰写回答