python脚本在Word文档中查找断开的链接
#python #自动化 #regex #microsoftword

我写了一个Python脚本,以在Word文档GitHub link is here中找到断开的链接。如果要使用它,则可以转到GitHub页面,并且说明在那里。在下面,我将解释它是如何工作的以及我如何提出该解决方案。

该程序的灵感是通过帮助朋友的启发。朋友必须单击文档中的链接,以检查它们是否仍在工作。我想着自己,听起来这听起来像是可以自动化的任务。因此,我要求一些示例文档并开始测试概念。

面对这项艰巨的任务,我决定将任务分为几个步骤,找出一种方法,然后将它们拼凑在一起。

步骤1:从文本中找到链接

如果我想从文本中提取一些模式,那么弹出的第一件事就是正则表达式。它们是在文本中找到模式的一种方式,通常被开发人员认为困难和恐惧。因此,我做了任何理智的开发人员都会做的事情:在线搜索此问题,并从堆栈溢出中复制了正则正则。

正则是(https?:\/\/\S+),很容易理解

|正则|含义|
| http | http |
| s? | s零至一次|
| :// | ://, /代表逃脱 / |
| \ s |任何非Whitespace字符|
| + |从零到无限时间|

的前一个令牌,\ s

问题1:随后的全场也匹配

假设我有一个以链接结束的句子,例如http://chit.hashnode.com。如果我在上面的正则分析本文中,也将包括最后一个全停(。),因为它是一个非Whitespace字符。

解决方案是使用另一条正则http[s]?:\/\/[^\s]+[^.],在这里我们有一个[^.],这意味着:匹配一个字符而不是周期(。)而不是whitespace(),因此它解决了它捕获尾随周期的问题。<<<<<<<<<<<<<<<<<<<<<<< /p>

问题2:结尾具有新线字符

以前的正则是在行结尾处有一个周期的问题。但是,如果之后有一个newline字符怎么办?

知识转储:什么是新线角色

newline字符是不可见的字符,它告诉文本编辑器转到下一行。例如,假设\n是Newline字符,I am a line.\nI am another line将成为

I am a line.
I am another line

让我们使用网站regex101对其进行测试,当我们拥有A sentence ending in https://google.com.时,https://google.com将被正确捕获。

但是,在此之后,它也将被捕获,因为newline字符在[^.]中没有排除。

当我写这篇博客文章时,我认为解决方案也是排除新线角色,从而导致了regex http[s]?:\/\/[^\s]+[^.],但是当我编程时,我并不明确,我想出的解决方案是用太空栏替换所有Newline字符。然后进行正则匹配,因为不再有newline字符。

问题3:不同类型的链接

第三个问题是链接,没有http,我们的正则是从http开始匹配的链接,但是有些链接以www开头,甚至没有www

我敢肯定,更聪明的正则可以容纳这一点,但是我不想在这个阶段花太多时间,所以我只是使用python库为我做这项工作。这就是Python的美/问题,有很多库,您可以使用一个库,而不在乎它是如何实现的。

from urlextract import URLExtract
urlextracter = URLExtract()
urls = urlextracter.gen_urls(s)

步骤2检查链接

现在,我们已经提取了文本中的所有URL,我们想检查它们。

问题4:未指定协议的链接

但是记得有些链接如何从HTTP开头?我需要在它们面前添加HTTP,否则Python库请求将很难知道需要什么协议,因此我使用以下代码来实现该协议


import re
def formaturl(url):
    if not re.match('(?:http|ftp|https)://', url):
        return 'http://{}'.format(url)
    return url

urls = [formaturl(url) for url in urls]

在这里,我尝试匹配正则(?:http|ftp|https)://,它测试URL是否以http/ftp/https开头,如果没有,我们在其前面附加了http://

然后,我们使用列表理解在整个URL列表上进行。

实际检查链接

现在,我们进入了实际检查链接的步骤,我们使用requests库来做到这一点。我们将requests.get()包装在一个trycatch块中,以便如果发生其他问题,该程序不会崩溃,它将简单地返回false。如果响应的状态_code不是200,那么我们也返回false,否则我们返回true。

200意味着一切都很好,因此,如果网站返回标准内容,它将返回所有内容。

def check_link(url):
    print(f"checking {url}")

    # Try and see if url have inherit problem
    try:
        response = requests.get(url)
    except:
        return False

    # See if not 200
    if not response.status_code == 200:
        return False

    return True

步骤3:阅读Word文档文本

现在,我有一个提取URL的函数,另一个可以检查URL的网站是否正常工作。我必须从Word文档中添加文本。

为此,我可以简单地使用Word中的保存AS功能,然后将其称为一天。这将以文本格式保存文档,我们可以使用程序读取文本文件并获得结果。

,但这意味着用户必须做更多的事情,所以我在想,可以直接使用Python读取Word Document?

答案是肯定的,因为实际上,Word文档只是zip文件。要知道我是否告诉您真相,请安装7zip并解压缩文档,您将看到以下结果。 Word文档只是一个包含许多XML文件的zip文件。

解决此问题的一种方法是将Word文档读取为zip文件,并在XML文件中找到链接。但是,由于我正在使用Python,因此我决定找到是否有任何库可以为我做到这一点。我找到了docxdocx2txtdocx2python。在测试所有内容之后,我决定使用的是docx2python,因为它提取了主要文本,标题,页脚甚至脚注。语法如下:

from docx2python import docx2python

# extract docx content
def get_text_by_docx2python(path):
    text = docx2python(path).text
    return text

步骤4将它们拼凑在一起

现在,难题的所有部分都在这里,现在该实现主要逻辑了,我们首先提取use_docx2python.get_text_by_docx2python的文本,然后是urlextracter.gen_urls来获取URL,在将http添加到它们之后,我们使用check_links.check_link检查链接并打印有用的消息。

问题:当我们从程序中请求该链接与实际单击网站时,有些链接显示出不同的结果

这是因为某些网站不喜欢机器人,但是由于Python请求将默认用户代理作为python-requests/2.25,因此该网站会看到并禁止该程序获得结果。我们需要做的解决方法是使用另一个用户代理,我在Web浏览器Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36上复制了用户代理,然后我使用了response = requests.get(url, headers=HEADERS, stream=True),现在网站正确响应。

步骤5不同步

现在等待程序完成所有事情都很好,但是也太慢了。最慢的部分是等待网站获取。因为我们必须等待每个网站为我们提供结果。如果我们使用并发,这将更快,这意味着同时做事。

将其视为,Burger Shop A在5分钟内为您提供汉堡,Boba Shop B在3分钟内为您提供了Boba。您可以在8分钟内获得汉堡,然后再获得Boba。如果您同时订购两者,然后在准备就绪时收集它们,则只需要5分钟。

并发代码更为复杂,所以我不会在这里解释,您可以就此主题访问RealPython,他们在此主题上有一个很棒的教程。

步骤6扭曲

最后,我添加了一个代码来显示文件对话,以进一步简化用户。

import tkinter as tk
from tkinter import filedialog
file_path = filedialog.askopenfilename()

结论

这就是我的做法,希望您从中学到一些东西。