prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add more extensions to IGNORED_EXTENSIONS
  A follow-up to https://github.com/scrapy/scrapy/pull/1835. For the record, in my last project I had to also add the following extensions to ignored:
  
  ```
  {
      '7z', '7zip', 'xz', 'gz', 'tar', 'bz2',   # archives
      'cdr',  # Corel Draw files
      'apk', # Android packages
  }
  ```
  
  cdr and apk look safe; I wonder if we can add .gz: Scrapy decompresses gz responses automatically, so if we add .gz to `IGNORED_EXTENSIONS` spider may stop following e.g. sitemap.xml.gz links. 
  
  On the other hand, for broad crawls most .gz files are archives, and we're filtering out .zip files already.
  
  Updated the list of ignored extension in the linkextractor component
  - Added following extension to the list: .7z, .7zip, .bz2, .gz, .tar, .xz, .cdr, .ico, .apk
  - Moved archives into the separate section
  
  See scrapy/scrapy#1837 issue for details and discussion.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `scrapy/linkextractors/__init__.py`
  Content:
  ```
  1 """
  2 scrapy.linkextractors
  3 
  4 This package contains a collection of Link Extractors.
  5 
  6 For more info see docs/topics/link-extractors.rst
  7 """
  8 import re
  9 
  10 from six.moves.urllib.parse import urlparse
  11 from parsel.csstranslator import HTMLTranslator
  12 from w3lib.url import canonicalize_url
  13 
  14 from scrapy.utils.misc import arg_to_iter
  15 from scrapy.utils.url import (
  16     url_is_from_any_domain, url_has_any_extension,
  17 )
  18 
  19 
  20 # common file extensions that are not followed if they occur in links
  21 IGNORED_EXTENSIONS = [
  22     # images
  23     'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',
  24     'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg',
  25 
  26     # audio
  27     'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',
  28 
  29     # video
  30     '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',
  31     'm4a', 'm4v', 'flv',
  32 
  33     # office suites
  34     'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',
  35     'odp',
  36 
  37     # other
  38     'css', 'pdf', 'exe', 'bin', 'rss', 'zip', 'rar',
  39 ]
  40 
  41 
  42 _re_type = type(re.compile("", 0))
  43 _matches = lambda url, regexs: any(r.search(url) for r in regexs)
  44 _is_valid_url = lambda url: url.split('://', 1)[0] in {'http', 'https', \
  45                                                        'file', 'ftp'}
  46 
  47 
  48 class FilteringLinkExtractor(object):
  49 
  50     _csstranslator = HTMLTranslator()
  51 
  52     def __init__(self, link_extractor, allow, deny, allow_domains, deny_domains,
  53                  restrict_xpaths, canonicalize, deny_extensions, restrict_css, restrict_text):
  54 
  55         self.link_extractor = link_extractor
  56 
  57         self.allow_res = [x if isinstance(x, _re_type) else re.compile(x)
  58                           for x in arg_to_iter(allow)]
  59         self.deny_res = [x if isinstance(x, _re_type) else re.compile(x)
  60                          for x in arg_to_iter(deny)]
  61 
  62         self.allow_domains = set(arg_to_iter(allow_domains))
  63         self.deny_domains = set(arg_to_iter(deny_domains))
  64 
  65         self.restrict_xpaths = tuple(arg_to_iter(restrict_xpaths))
  66         self.restrict_xpaths += tuple(map(self._csstranslator.css_to_xpath,
  67                                           arg_to_iter(restrict_css)))
  68 
  69         self.canonicalize = canonicalize
  70         if deny_extensions is None:
  71             deny_extensions = IGNORED_EXTENSIONS
  72         self.deny_extensions = {'.' + e for e in arg_to_iter(deny_extensions)}
  73         self.restrict_text = [x if isinstance(x, _re_type) else re.compile(x)
  74                               for x in arg_to_iter(restrict_text)]
  75 
  76     def _link_allowed(self, link):
  77         if not _is_valid_url(link.url):
  78             return False
  79         if self.allow_res and not _matches(link.url, self.allow_res):
  80             return False
  81         if self.deny_res and _matches(link.url, self.deny_res):
  82             return False
  83         parsed_url = urlparse(link.url)
  84         if self.allow_domains and not url_is_from_any_domain(parsed_url, self.allow_domains):
  85             return False
  86         if self.deny_domains and url_is_from_any_domain(parsed_url, self.deny_domains):
  87             return False
  88         if self.deny_extensions and url_has_any_extension(parsed_url, self.deny_extensions):
  89             return False
  90         if self.restrict_text and not _matches(link.text, self.restrict_text):
  91             return False
  92         return True
  93 
  94     def matches(self, url):
  95 
  96         if self.allow_domains and not url_is_from_any_domain(url, self.allow_domains):
  97             return False
  98         if self.deny_domains and url_is_from_any_domain(url, self.deny_domains):
  99             return False
  100 
  101         allowed = (regex.search(url) for regex in self.allow_res) if self.allow_res else [True]
  102         denied = (regex.search(url) for regex in self.deny_res) if self.deny_res else []
  103         return any(allowed) and not any(denied)
  104 
  105     def _process_links(self, links):
  106         links = [x for x in links if self._link_allowed(x)]
  107         if self.canonicalize:
  108             for link in links:
  109                 link.url = canonicalize_url(link.url)
  110         links = self.link_extractor._process_links(links)
  111         return links
  112 
  113     def _extract_links(self, *args, **kwargs):
  114         return self.link_extractor._extract_links(*args, **kwargs)
  115 
  116 
  117 # Top-level imports
  118 from .lxmlhtml import LxmlLinkExtractor as LinkExtractor
  119
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/scrapy/linkextractors/__init__.py b/scrapy/linkextractors/__init__.py
  --- a/scrapy/linkextractors/__init__.py
  +++ b/scrapy/linkextractors/__init__.py
  @@ -19,23 +19,26 @@
   
   # common file extensions that are not followed if they occur in links
   IGNORED_EXTENSIONS = [
  +    # archives
  +    '7z', '7zip', 'bz2', 'rar', 'tar', 'tar.gz', 'xz', 'zip',
  +
       # images
       'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',
  -    'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg',
  +    'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg', 'cdr', 'ico',
   
       # audio
       'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',
   
       # video
       '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',
  -    'm4a', 'm4v', 'flv',
  +    'm4a', 'm4v', 'flv', 'webm',
   
       # office suites
       'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',
       'odp',
   
       # other
  -    'css', 'pdf', 'exe', 'bin', 'rss', 'zip', 'rar',
  +    'css', 'pdf', 'exe', 'bin', 'rss', 'dmg', 'iso', 'apk'
   ]
  
verification_info: {"golden_diff": "diff --git a/scrapy/linkextractors/__init__.py b/scrapy/linkextractors/__init__.py\n--- a/scrapy/linkextractors/__init__.py\n+++ b/scrapy/linkextractors/__init__.py\n@@ -19,23 +19,26 @@\n \n # common file extensions that are not followed if they occur in links\n IGNORED_EXTENSIONS = [\n+    # archives\n+    '7z', '7zip', 'bz2', 'rar', 'tar', 'tar.gz', 'xz', 'zip',\n+\n     # images\n     'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',\n-    'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg',\n+    'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg', 'cdr', 'ico',\n \n     # audio\n     'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',\n \n     # video\n     '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',\n-    'm4a', 'm4v', 'flv',\n+    'm4a', 'm4v', 'flv', 'webm',\n \n     # office suites\n     'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',\n     'odp',\n \n     # other\n-    'css', 'pdf', 'exe', 'bin', 'rss', 'zip', 'rar',\n+    'css', 'pdf', 'exe', 'bin', 'rss', 'dmg', 'iso', 'apk'\n ]\n", "issue": "Add more extensions to IGNORED_EXTENSIONS\nA follow-up to https://github.com/scrapy/scrapy/pull/1835. For the record, in my last project I had to also add the following extensions to ignored:\n\n```\n{\n    '7z', '7zip', 'xz', 'gz', 'tar', 'bz2',   # archives\n    'cdr',  # Corel Draw files\n    'apk', # Android packages\n}\n```\n\ncdr and apk look safe; I wonder if we can add .gz: Scrapy decompresses gz responses automatically, so if we add .gz to `IGNORED_EXTENSIONS` spider may stop following e.g. sitemap.xml.gz links. \n\nOn the other hand, for broad crawls most .gz files are archives, and we're filtering out .zip files already.\n\nUpdated the list of ignored extension in the linkextractor component\n- Added following extension to the list: .7z, .7zip, .bz2, .gz, .tar, .xz, .cdr, .ico, .apk\n- Moved archives into the separate section\n\nSee scrapy/scrapy#1837 issue for details and discussion.\n\n", "before_files": [{"content": "\"\"\"\nscrapy.linkextractors\n\nThis package contains a collection of Link Extractors.\n\nFor more info see docs/topics/link-extractors.rst\n\"\"\"\nimport re\n\nfrom six.moves.urllib.parse import urlparse\nfrom parsel.csstranslator import HTMLTranslator\nfrom w3lib.url import canonicalize_url\n\nfrom scrapy.utils.misc import arg_to_iter\nfrom scrapy.utils.url import (\n    url_is_from_any_domain, url_has_any_extension,\n)\n\n\n# common file extensions that are not followed if they occur in links\nIGNORED_EXTENSIONS = [\n    # images\n    'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',\n    'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg',\n\n    # audio\n    'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',\n\n    # video\n    '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',\n    'm4a', 'm4v', 'flv',\n\n    # office suites\n    'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',\n    'odp',\n\n    # other\n    'css', 'pdf', 'exe', 'bin', 'rss', 'zip', 'rar',\n]\n\n\n_re_type = type(re.compile(\"\", 0))\n_matches = lambda url, regexs: any(r.search(url) for r in regexs)\n_is_valid_url = lambda url: url.split('://', 1)[0] in {'http', 'https', \\\n                                                       'file', 'ftp'}\n\n\nclass FilteringLinkExtractor(object):\n\n    _csstranslator = HTMLTranslator()\n\n    def __init__(self, link_extractor, allow, deny, allow_domains, deny_domains,\n                 restrict_xpaths, canonicalize, deny_extensions, restrict_css, restrict_text):\n\n        self.link_extractor = link_extractor\n\n        self.allow_res = [x if isinstance(x, _re_type) else re.compile(x)\n                          for x in arg_to_iter(allow)]\n        self.deny_res = [x if isinstance(x, _re_type) else re.compile(x)\n                         for x in arg_to_iter(deny)]\n\n        self.allow_domains = set(arg_to_iter(allow_domains))\n        self.deny_domains = set(arg_to_iter(deny_domains))\n\n        self.restrict_xpaths = tuple(arg_to_iter(restrict_xpaths))\n        self.restrict_xpaths += tuple(map(self._csstranslator.css_to_xpath,\n                                          arg_to_iter(restrict_css)))\n\n        self.canonicalize = canonicalize\n        if deny_extensions is None:\n            deny_extensions = IGNORED_EXTENSIONS\n        self.deny_extensions = {'.' + e for e in arg_to_iter(deny_extensions)}\n        self.restrict_text = [x if isinstance(x, _re_type) else re.compile(x)\n                              for x in arg_to_iter(restrict_text)]\n\n    def _link_allowed(self, link):\n        if not _is_valid_url(link.url):\n            return False\n        if self.allow_res and not _matches(link.url, self.allow_res):\n            return False\n        if self.deny_res and _matches(link.url, self.deny_res):\n            return False\n        parsed_url = urlparse(link.url)\n        if self.allow_domains and not url_is_from_any_domain(parsed_url, self.allow_domains):\n            return False\n        if self.deny_domains and url_is_from_any_domain(parsed_url, self.deny_domains):\n            return False\n        if self.deny_extensions and url_has_any_extension(parsed_url, self.deny_extensions):\n            return False\n        if self.restrict_text and not _matches(link.text, self.restrict_text):\n            return False\n        return True\n\n    def matches(self, url):\n\n        if self.allow_domains and not url_is_from_any_domain(url, self.allow_domains):\n            return False\n        if self.deny_domains and url_is_from_any_domain(url, self.deny_domains):\n            return False\n\n        allowed = (regex.search(url) for regex in self.allow_res) if self.allow_res else [True]\n        denied = (regex.search(url) for regex in self.deny_res) if self.deny_res else []\n        return any(allowed) and not any(denied)\n\n    def _process_links(self, links):\n        links = [x for x in links if self._link_allowed(x)]\n        if self.canonicalize:\n            for link in links:\n                link.url = canonicalize_url(link.url)\n        links = self.link_extractor._process_links(links)\n        return links\n\n    def _extract_links(self, *args, **kwargs):\n        return self.link_extractor._extract_links(*args, **kwargs)\n\n\n# Top-level imports\nfrom .lxmlhtml import LxmlLinkExtractor as LinkExtractor\n", "path": "scrapy/linkextractors/__init__.py"}], "after_files": [{"content": "\"\"\"\nscrapy.linkextractors\n\nThis package contains a collection of Link Extractors.\n\nFor more info see docs/topics/link-extractors.rst\n\"\"\"\nimport re\n\nfrom six.moves.urllib.parse import urlparse\nfrom parsel.csstranslator import HTMLTranslator\nfrom w3lib.url import canonicalize_url\n\nfrom scrapy.utils.misc import arg_to_iter\nfrom scrapy.utils.url import (\n    url_is_from_any_domain, url_has_any_extension,\n)\n\n\n# common file extensions that are not followed if they occur in links\nIGNORED_EXTENSIONS = [\n    # archives\n    '7z', '7zip', 'bz2', 'rar', 'tar', 'tar.gz', 'xz', 'zip',\n\n    # images\n    'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',\n    'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg', 'cdr', 'ico',\n\n    # audio\n    'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',\n\n    # video\n    '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',\n    'm4a', 'm4v', 'flv', 'webm',\n\n    # office suites\n    'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',\n    'odp',\n\n    # other\n    'css', 'pdf', 'exe', 'bin', 'rss', 'dmg', 'iso', 'apk'\n]\n\n\n_re_type = type(re.compile(\"\", 0))\n_matches = lambda url, regexs: any(r.search(url) for r in regexs)\n_is_valid_url = lambda url: url.split('://', 1)[0] in {'http', 'https', \\\n                                                       'file', 'ftp'}\n\n\nclass FilteringLinkExtractor(object):\n\n    _csstranslator = HTMLTranslator()\n\n    def __init__(self, link_extractor, allow, deny, allow_domains, deny_domains,\n                 restrict_xpaths, canonicalize, deny_extensions, restrict_css, restrict_text):\n\n        self.link_extractor = link_extractor\n\n        self.allow_res = [x if isinstance(x, _re_type) else re.compile(x)\n                          for x in arg_to_iter(allow)]\n        self.deny_res = [x if isinstance(x, _re_type) else re.compile(x)\n                         for x in arg_to_iter(deny)]\n\n        self.allow_domains = set(arg_to_iter(allow_domains))\n        self.deny_domains = set(arg_to_iter(deny_domains))\n\n        self.restrict_xpaths = tuple(arg_to_iter(restrict_xpaths))\n        self.restrict_xpaths += tuple(map(self._csstranslator.css_to_xpath,\n                                          arg_to_iter(restrict_css)))\n\n        self.canonicalize = canonicalize\n        if deny_extensions is None:\n            deny_extensions = IGNORED_EXTENSIONS\n        self.deny_extensions = {'.' + e for e in arg_to_iter(deny_extensions)}\n        self.restrict_text = [x if isinstance(x, _re_type) else re.compile(x)\n                              for x in arg_to_iter(restrict_text)]\n\n    def _link_allowed(self, link):\n        if not _is_valid_url(link.url):\n            return False\n        if self.allow_res and not _matches(link.url, self.allow_res):\n            return False\n        if self.deny_res and _matches(link.url, self.deny_res):\n            return False\n        parsed_url = urlparse(link.url)\n        if self.allow_domains and not url_is_from_any_domain(parsed_url, self.allow_domains):\n            return False\n        if self.deny_domains and url_is_from_any_domain(parsed_url, self.deny_domains):\n            return False\n        if self.deny_extensions and url_has_any_extension(parsed_url, self.deny_extensions):\n            return False\n        if self.restrict_text and not _matches(link.text, self.restrict_text):\n            return False\n        return True\n\n    def matches(self, url):\n\n        if self.allow_domains and not url_is_from_any_domain(url, self.allow_domains):\n            return False\n        if self.deny_domains and url_is_from_any_domain(url, self.deny_domains):\n            return False\n\n        allowed = (regex.search(url) for regex in self.allow_res) if self.allow_res else [True]\n        denied = (regex.search(url) for regex in self.deny_res) if self.deny_res else []\n        return any(allowed) and not any(denied)\n\n    def _process_links(self, links):\n        links = [x for x in links if self._link_allowed(x)]\n        if self.canonicalize:\n            for link in links:\n                link.url = canonicalize_url(link.url)\n        links = self.link_extractor._process_links(links)\n        return links\n\n    def _extract_links(self, *args, **kwargs):\n        return self.link_extractor._extract_links(*args, **kwargs)\n\n\n# Top-level imports\nfrom .lxmlhtml import LxmlLinkExtractor as LinkExtractor\n", "path": "scrapy/linkextractors/__init__.py"}]}