如何使用Python、HTML解析器和正则表达式提取URL

3
我需要用Python编写一个程序,解析一个.html文件中的所有URL,并打印出所有标签和链接,格式如下:
meta: https://someurl.com
a: https://someurl.com
link: css/bootstrap.min.css
script: https://somescript.js

目前,我所拥有的是

from html.parser import HTMLParser
import re

class HeadParser(HTMLParser):

def handle_starttag(self, tag, attrs):
    #use re.findall to get all the links
    links = re.findall("http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\), ]|(?:%[0-9a-fA-F][0-9a-fA-F]))+", website)
    for url in links:
        print("{0}: {1}".format(tag, url))

website = open("./head.html").read()            
HeadParser().feed(website)

并且它会返回给我。
head: https://scooptacular.net
head: https://scooptacular.net/img/uploaded/379d05029c0d84618c70ac037a25fd88.jpg
head: https://scooptacular.net/img/uploaded/4baaa58a1a37fd3da3e4e78caf366b7f.jpg
head: https://fonts.googleapis.com/css?family=Montserrat:400,700
head: https://fonts.googleapis.com/css?family=Kaushan+Script' rel='stylesheet' type='text/css'>
head: https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
head: https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700' rel='stylesheet' type='text/css'>
head: https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js
head: https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js
meta: https://scooptacular.net
meta: https://scooptacular.net/img/uploaded/379d05029c0d84618c70ac037a25fd88.jpg
meta: https://scooptacular.net/img/uploaded/4baaa58a1a37fd3da3e4e78caf366b7f.jpg
meta: https://fonts.googleapis.com/css?family=Montserrat:400,700
meta: https://fonts.googleapis.com/css?family=Kaushan+Script' rel='stylesheet' type='text/css'>
meta: https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
meta: https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700' rel='stylesheet' type='text/css'>
meta: https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js
meta: https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js
meta: https://scooptacular.net
meta: https://scooptacular.net/img/uploaded/379d05029c0d84618c70

如您所见,它为每个标签返回了一个链接,甚至是重复的,并且没有返回任何本地文件链接。我的代码有什么问题吗?

编辑:

我使用的html是:

<head>
<meta property="og:url" content="https://scooptacular.net" />    
<meta property="og:image" content="https://scooptacular.net/img/uploaded/379d05029c0d84618c70ac037a25fd88.jpg" />
<meta property="og:image" content="https://scooptacular.net/img/uploaded/4baaa58a1a37fd3da3e4e78caf366b7f.jpg" />

<link href="css/bootstrap.min.css" rel="stylesheet">

<link href="css/agency.css" rel="stylesheet">

<link href="font-awesome-4.1.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">

        <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css">
    <link href='https://fonts.googleapis.com/css?family=Kaushan+Script' rel='stylesheet' type='text/css'>
    <link href='https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link href='https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700' rel='stylesheet' type='text/css'>


<link href="css/bootstrap-formhelpers.min.css" rel="stylesheet" media="screen">

    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
</head>

看起来你已经得到了你想要的——所有的网址。重复可能只是在那个网页中发生了多次。哪些是你没有预料到的? - Jongware
在“head”标签内部放置链接没有任何意义(不存在<head href="">),那么为什么它会被打印出来呢? - Erika N
你可以使用 set(links) 来去除重复的链接。同时,你需要注意的是,代码会默认获取到第一个标签,而你需要的是在 <head 标签内的标签。 - wishmaster
1
你能否提供一些你正在处理的链接的示例?目前,我使用了__http(|s):[^'$\s]+__,看起来可以工作(它获取了所有链接,但让我们尝试一些更通用的东西)。注意:我假设所有链接都以http或https开头。 https://regex101.com/r/h1Y8Cu - lucas_7_94
@lucas_7_94 我编辑了帖子,包括我正在处理的链接。并非所有链接都以http/s开头。 - Erika N
1个回答

1
主要问题在于对于每个标签都会调用handle_starttag,并且每次调用都会在整个页面中搜索与您的正则表达式匹配的内容,而不仅仅是您正在处理的标签(您传递给re.findall的第二个参数是website)。
我不明白为什么您需要在这里使用正则表达式。为什么不仅仅依赖于标签是否具有hrefsrccontent属性:
from html.parser import HTMLParser


class HeadParser(HTMLParser):

    def handle_starttag(self, tag, attrs):
        for attr in attrs:
            if attr[0] in ['href', 'src', 'content']:
                print('{0}: {1}'.format(tag, attr[1]))


website = open("./head.html").read()
HeadParser().feed(website)

输出:

meta: https://scooptacular.net
meta: https://scooptacular.net/img/uploaded/379d05029c0d84618c70ac037a25fd88.jpg
meta: https://scooptacular.net/img/uploaded/4baaa58a1a37fd3da3e4e78caf366b7f.jpg
link: css/bootstrap.min.css
link: css/agency.css
link: font-awesome-4.1.0/css/font-awesome.min.css
link: https://fonts.googleapis.com/css?family=Montserrat:400,700
link: https://fonts.googleapis.com/css?family=Kaushan+Script
link: https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic
link: https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700
link: css/bootstrap-formhelpers.min.css
script: https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js
script: https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接