We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove `streamlink.plugin.api.utils.itertags`
[`streamlink.plugin.api.utils.itertags`](https://github.com/streamlink/streamlink/blob/3.2.0/src/streamlink/plugin/api/utils.py#L16-L28) has become obsolete ever since `lxml` was added as a dependency to Streamlink for parsing HTML.

`itertags` is a hacky implementation via regexes, which is not only slow, but it's also impossible to correctly parse HTML nodes with regular expressions, so it shouldn't be used when better and much faster solutions are available. It also always requires unescaping tag values, which is annoying.

We've already updated and replaced lots of plugins which were previously using it, but there are still some left:
```
$ GIT_PAGER=cat git grep -F 'from streamlink.plugin.api.utils import' a1ce471f
a1ce471f:src/streamlink/plugins/cdnbg.py:from streamlink.plugin.api.utils import itertags
a1ce471f:src/streamlink/plugins/facebook.py:from streamlink.plugin.api.utils import itertags
a1ce471f:src/streamlink/plugins/funimationnow.py:from streamlink.plugin.api.utils import itertags
a1ce471f:src/streamlink/plugins/senategov.py:from streamlink.plugin.api.utils import itertags
a1ce471f:src/streamlink/plugins/vrtbe.py:from streamlink.plugin.api.utils import itertags
a1ce471f:tests/test_plugin_utils.py:from streamlink.plugin.api.utils import itertags
```

- [x] cdnbg
- [x] facebook
- [x] funimationnow
- [x] senategov
- [x] vrtbe

Once every last plugin has been updated, the entire `streamlink.plugin.api.utils` module can be removed, as it only contains the `itertags` function and some other useless export aliases which are not even used anymore in Streamlink's codebase.

If we care about plugin-API stability (something which has never been discussed), removing this would be considered a breaking change. Since we've just dropped py36, that's something which could be included in the 4.0.0 release.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/streamlink/plugin/api/utils.py`
Content:
```
1 """Useful wrappers and other tools."""
2 import re
3 from collections import namedtuple
4 
5 from streamlink.utils.parse import parse_json, parse_qsd as parse_query, parse_xml
6 
7 __all__ = ["parse_json", "parse_xml", "parse_query"]
8 
9 
10 tag_re = re.compile(r'''(?=<(?P<tag>[a-zA-Z]+)(?P<attr>.*?)(?P<end>/)?>(?:(?P<inner>.*?)</\s*(?P=tag)\s*>)?)''',
11                     re.MULTILINE | re.DOTALL)
12 attr_re = re.compile(r'''\s*(?P<key>[\w-]+)\s*(?:=\s*(?P<quote>["']?)(?P<value>.*?)(?P=quote)\s*)?''')
13 Tag = namedtuple("Tag", "tag attributes text")
14 
15 
16 def itertags(html, tag):
17     """
18     Brute force regex based HTML tag parser. This is a rough-and-ready searcher to find HTML tags when
19     standards compliance is not required. Will find tags that are commented out, or inside script tag etc.
20 
21     :param html: HTML page
22     :param tag: tag name to find
23     :return: generator with Tags
24     """
25     for match in tag_re.finditer(html):
26         if match.group("tag") == tag:
27             attrs = {a.group("key").lower(): a.group("value") for a in attr_re.finditer(match.group("attr"))}
28             yield Tag(match.group("tag"), attrs, match.group("inner"))
29
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/streamlink/plugin/api/utils.py b/src/streamlink/plugin/api/utils.py
deleted file mode 100644
--- a/src/streamlink/plugin/api/utils.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""Useful wrappers and other tools."""
-import re
-from collections import namedtuple
-
-from streamlink.utils.parse import parse_json, parse_qsd as parse_query, parse_xml
-
-__all__ = ["parse_json", "parse_xml", "parse_query"]
-
-
-tag_re = re.compile(r'''(?=<(?P<tag>[a-zA-Z]+)(?P<attr>.*?)(?P<end>/)?>(?:(?P<inner>.*?)</\s*(?P=tag)\s*>)?)''',
-                    re.MULTILINE | re.DOTALL)
-attr_re = re.compile(r'''\s*(?P<key>[\w-]+)\s*(?:=\s*(?P<quote>["']?)(?P<value>.*?)(?P=quote)\s*)?''')
-Tag = namedtuple("Tag", "tag attributes text")
-
-
-def itertags(html, tag):
-    """
-    Brute force regex based HTML tag parser. This is a rough-and-ready searcher to find HTML tags when
-    standards compliance is not required. Will find tags that are commented out, or inside script tag etc.
-
-    :param html: HTML page
-    :param tag: tag name to find
-    :return: generator with Tags
-    """
-    for match in tag_re.finditer(html):
-        if match.group("tag") == tag:
-            attrs = {a.group("key").lower(): a.group("value") for a in attr_re.finditer(match.group("attr"))}
-            yield Tag(match.group("tag"), attrs, match.group("inner"))

{"golden_diff": "diff --git a/src/streamlink/plugin/api/utils.py b/src/streamlink/plugin/api/utils.py\ndeleted file mode 100644\n--- a/src/streamlink/plugin/api/utils.py\n+++ /dev/null\n@@ -1,28 +0,0 @@\n-\"\"\"Useful wrappers and other tools.\"\"\"\n-import re\n-from collections import namedtuple\n-\n-from streamlink.utils.parse import parse_json, parse_qsd as parse_query, parse_xml\n-\n-__all__ = [\"parse_json\", \"parse_xml\", \"parse_query\"]\n-\n-\n-tag_re = re.compile(r'''(?=<(?P<tag>[a-zA-Z]+)(?P<attr>.*?)(?P<end>/)?>(?:(?P<inner>.*?)</\\s*(?P=tag)\\s*>)?)''',\n-                    re.MULTILINE | re.DOTALL)\n-attr_re = re.compile(r'''\\s*(?P<key>[\\w-]+)\\s*(?:=\\s*(?P<quote>[\"']?)(?P<value>.*?)(?P=quote)\\s*)?''')\n-Tag = namedtuple(\"Tag\", \"tag attributes text\")\n-\n-\n-def itertags(html, tag):\n-    \"\"\"\n-    Brute force regex based HTML tag parser. This is a rough-and-ready searcher to find HTML tags when\n-    standards compliance is not required. Will find tags that are commented out, or inside script tag etc.\n-\n-    :param html: HTML page\n-    :param tag: tag name to find\n-    :return: generator with Tags\n-    \"\"\"\n-    for match in tag_re.finditer(html):\n-        if match.group(\"tag\") == tag:\n-            attrs = {a.group(\"key\").lower(): a.group(\"value\") for a in attr_re.finditer(match.group(\"attr\"))}\n-            yield Tag(match.group(\"tag\"), attrs, match.group(\"inner\"))\n", "issue": "Remove `streamlink.plugin.api.utils.itertags`\n[`streamlink.plugin.api.utils.itertags`](https://github.com/streamlink/streamlink/blob/3.2.0/src/streamlink/plugin/api/utils.py#L16-L28) has become obsolete ever since `lxml` was added as a dependency to Streamlink for parsing HTML.\r\n\r\n`itertags` is a hacky implementation via regexes, which is not only slow, but it's also impossible to correctly parse HTML nodes with regular expressions, so it shouldn't be used when better and much faster solutions are available. It also always requires unescaping tag values, which is annoying.\r\n\r\nWe've already updated and replaced lots of plugins which were previously using it, but there are still some left:\r\n```\r\n$ GIT_PAGER=cat git grep -F 'from streamlink.plugin.api.utils import' a1ce471f\r\na1ce471f:src/streamlink/plugins/cdnbg.py:from streamlink.plugin.api.utils import itertags\r\na1ce471f:src/streamlink/plugins/facebook.py:from streamlink.plugin.api.utils import itertags\r\na1ce471f:src/streamlink/plugins/funimationnow.py:from streamlink.plugin.api.utils import itertags\r\na1ce471f:src/streamlink/plugins/senategov.py:from streamlink.plugin.api.utils import itertags\r\na1ce471f:src/streamlink/plugins/vrtbe.py:from streamlink.plugin.api.utils import itertags\r\na1ce471f:tests/test_plugin_utils.py:from streamlink.plugin.api.utils import itertags\r\n```\r\n\r\n- [x] cdnbg\r\n- [x] facebook\r\n- [x] funimationnow\r\n- [x] senategov\r\n- [x] vrtbe\r\n\r\nOnce every last plugin has been updated, the entire `streamlink.plugin.api.utils` module can be removed, as it only contains the `itertags` function and some other useless export aliases which are not even used anymore in Streamlink's codebase.\r\n\r\nIf we care about plugin-API stability (something which has never been discussed), removing this would be considered a breaking change. Since we've just dropped py36, that's something which could be included in the 4.0.0 release.\n", "before_files": [{"content": "\"\"\"Useful wrappers and other tools.\"\"\"\nimport re\nfrom collections import namedtuple\n\nfrom streamlink.utils.parse import parse_json, parse_qsd as parse_query, parse_xml\n\n__all__ = [\"parse_json\", \"parse_xml\", \"parse_query\"]\n\n\ntag_re = re.compile(r'''(?=<(?P<tag>[a-zA-Z]+)(?P<attr>.*?)(?P<end>/)?>(?:(?P<inner>.*?)</\\s*(?P=tag)\\s*>)?)''',\n                    re.MULTILINE | re.DOTALL)\nattr_re = re.compile(r'''\\s*(?P<key>[\\w-]+)\\s*(?:=\\s*(?P<quote>[\"']?)(?P<value>.*?)(?P=quote)\\s*)?''')\nTag = namedtuple(\"Tag\", \"tag attributes text\")\n\n\ndef itertags(html, tag):\n    \"\"\"\n    Brute force regex based HTML tag parser. This is a rough-and-ready searcher to find HTML tags when\n    standards compliance is not required. Will find tags that are commented out, or inside script tag etc.\n\n    :param html: HTML page\n    :param tag: tag name to find\n    :return: generator with Tags\n    \"\"\"\n    for match in tag_re.finditer(html):\n        if match.group(\"tag\") == tag:\n            attrs = {a.group(\"key\").lower(): a.group(\"value\") for a in attr_re.finditer(match.group(\"attr\"))}\n            yield Tag(match.group(\"tag\"), attrs, match.group(\"inner\"))\n", "path": "src/streamlink/plugin/api/utils.py"}], "after_files": [{"content": null, "path": "src/streamlink/plugin/api/utils.py"}]}