prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  NewspowerAUSpider - can this be rewritten as a WPStoreLocator spider?
  ```
  import re
  from html import unescape
  
  from chompjs import parse_js_object
  from scrapy.spiders import SitemapSpider
  
  from locations.hours import OpeningHours
  from locations.items import Feature
  
  
  class NewspowerAUSpider(SitemapSpider):
      name = "newspower_au"
      item_attributes = {"brand": "Newspower", "brand_wikidata": "Q120670137"}
      allowed_domains = ["newspower.com.au"]
      sitemap_urls = [
          "https://newspower.com.au/wpsl_stores-sitemap1.xml",
          "https://newspower.com.au/wpsl_stores-sitemap2.xml",
      ]
      sitemap_rules = [("/stores/", "parse")]
  
      def parse(self, response):
          map_marker_js_blob = response.xpath('//script[contains(text(), "var wpslMap_0 = ")]/text()').get()
          map_marker_js_blob = map_marker_js_blob.split("var wpslMap_0 = ", 1)[1].split("]};", 1)[0] + "]}"
          map_marker_dict = parse_js_object(map_marker_js_blob)["locations"][0]
          properties = {
              "ref": map_marker_dict["id"],
              "name": response.xpath('//div[@class="wpsl-locations-details"]/span/strong/text()').get().strip(),
              "addr_full": unescape(
                  re.sub(
                      r"\s+",
                      " ",
                      ", ".join(filter(None, response.xpath('//div[@class="wpsl-location-address"]//text()').getall())),
                  )
              )
              .replace(" ,", ",")
              .strip(),
              "street_address": ", ".join(filter(None, [map_marker_dict["address"], map_marker_dict["address2"]])),
              "city": map_marker_dict["city"],
              "state": map_marker_dict["state"],
              "postcode": map_marker_dict["zip"],
              "lat": map_marker_dict["lat"],
              "lon": map_marker_dict["lng"],
              "phone": response.xpath('//div[@class="wpsl-contact-details"]//a[contains(@href, "tel:")]/@href').get(),
              "website": response.url,
              "facebook": response.xpath(
                  '//div[@class="entry-content"]//a[contains(@href, "https://www.facebook.com/")]/@href'
              ).get(),
          }
          if properties.get("phone") and "tel:" in properties.get("phone"):
              properties["phone"] = properties["phone"].replace("tel:", "")
          hours_string = " ".join(filter(None, response.xpath('//table[@class="wpsl-opening-hours"]//text()').getall()))
          properties["opening_hours"] = OpeningHours()
          properties["opening_hours"].add_ranges_from_string(hours_string)
          yield Feature(**properties)
  ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `locations/spiders/newspower_au.py`
  Content:
  ```
  1 import re
  2 from html import unescape
  3 
  4 from chompjs import parse_js_object
  5 from scrapy.spiders import SitemapSpider
  6 
  7 from locations.hours import OpeningHours
  8 from locations.items import Feature
  9 
  10 
  11 class NewspowerAUSpider(SitemapSpider):
  12     name = "newspower_au"
  13     item_attributes = {"brand": "Newspower", "brand_wikidata": "Q120670137"}
  14     allowed_domains = ["newspower.com.au"]
  15     sitemap_urls = [
  16         "https://newspower.com.au/wpsl_stores-sitemap1.xml",
  17         "https://newspower.com.au/wpsl_stores-sitemap2.xml",
  18     ]
  19     sitemap_rules = [("/stores/", "parse")]
  20 
  21     def parse(self, response):
  22         map_marker_js_blob = response.xpath('//script[contains(text(), "var wpslMap_0 = ")]/text()').get()
  23         map_marker_js_blob = map_marker_js_blob.split("var wpslMap_0 = ", 1)[1].split("]};", 1)[0] + "]}"
  24         map_marker_dict = parse_js_object(map_marker_js_blob)["locations"][0]
  25         properties = {
  26             "ref": map_marker_dict["id"],
  27             "name": response.xpath('//div[@class="wpsl-locations-details"]/span/strong/text()').get().strip(),
  28             "addr_full": unescape(
  29                 re.sub(
  30                     r"\s+",
  31                     " ",
  32                     ", ".join(filter(None, response.xpath('//div[@class="wpsl-location-address"]//text()').getall())),
  33                 )
  34             )
  35             .replace(" ,", ",")
  36             .strip(),
  37             "street_address": ", ".join(filter(None, [map_marker_dict["address"], map_marker_dict["address2"]])),
  38             "city": map_marker_dict["city"],
  39             "state": map_marker_dict["state"],
  40             "postcode": map_marker_dict["zip"],
  41             "lat": map_marker_dict["lat"],
  42             "lon": map_marker_dict["lng"],
  43             "phone": response.xpath('//div[@class="wpsl-contact-details"]//a[contains(@href, "tel:")]/@href').get(),
  44             "website": response.url,
  45             "facebook": response.xpath(
  46                 '//div[@class="entry-content"]//a[contains(@href, "https://www.facebook.com/")]/@href'
  47             ).get(),
  48         }
  49         if properties.get("phone") and "tel:" in properties.get("phone"):
  50             properties["phone"] = properties["phone"].replace("tel:", "")
  51         hours_string = " ".join(filter(None, response.xpath('//table[@class="wpsl-opening-hours"]//text()').getall()))
  52         properties["opening_hours"] = OpeningHours()
  53         properties["opening_hours"].add_ranges_from_string(hours_string)
  54         yield Feature(**properties)
  55
  ```
  --- 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/locations/spiders/newspower_au.py b/locations/spiders/newspower_au.py
  --- a/locations/spiders/newspower_au.py
  +++ b/locations/spiders/newspower_au.py
  @@ -9,6 +9,11 @@
   
   
   class NewspowerAUSpider(SitemapSpider):
  +    # Whilst WP Store Locator is used for this brand, it is set to
  +    # return at most the 5 closest points to a provided search
  +    # coordinate. There is an impractical number of search requests
  +    # thus required to use the WP Store Locator store finder API.
  +    # A Sitemap spider is used instead.
       name = "newspower_au"
       item_attributes = {"brand": "Newspower", "brand_wikidata": "Q120670137"}
       allowed_domains = ["newspower.com.au"]
  @@ -16,7 +21,11 @@
           "https://newspower.com.au/wpsl_stores-sitemap1.xml",
           "https://newspower.com.au/wpsl_stores-sitemap2.xml",
       ]
  -    sitemap_rules = [("/stores/", "parse")]
  +    sitemap_rules = [(r"^https:\/\/newspower\.com\.au\/stores/[^/]+\/$", "parse")]
  +    # Server will redirect wpsl_stores-sitemap2.xml to
  +    # https://newspower.com.au/store-locator/ if it doesn't like
  +    # the country/netblock requesting the page.
  +    requires_proxy = True
   
       def parse(self, response):
           map_marker_js_blob = response.xpath('//script[contains(text(), "var wpslMap_0 = ")]/text()').get()
  
verification_info: {"golden_diff": "diff --git a/locations/spiders/newspower_au.py b/locations/spiders/newspower_au.py\n--- a/locations/spiders/newspower_au.py\n+++ b/locations/spiders/newspower_au.py\n@@ -9,6 +9,11 @@\n \n \n class NewspowerAUSpider(SitemapSpider):\n+    # Whilst WP Store Locator is used for this brand, it is set to\n+    # return at most the 5 closest points to a provided search\n+    # coordinate. There is an impractical number of search requests\n+    # thus required to use the WP Store Locator store finder API.\n+    # A Sitemap spider is used instead.\n     name = \"newspower_au\"\n     item_attributes = {\"brand\": \"Newspower\", \"brand_wikidata\": \"Q120670137\"}\n     allowed_domains = [\"newspower.com.au\"]\n@@ -16,7 +21,11 @@\n         \"https://newspower.com.au/wpsl_stores-sitemap1.xml\",\n         \"https://newspower.com.au/wpsl_stores-sitemap2.xml\",\n     ]\n-    sitemap_rules = [(\"/stores/\", \"parse\")]\n+    sitemap_rules = [(r\"^https:\\/\\/newspower\\.com\\.au\\/stores/[^/]+\\/$\", \"parse\")]\n+    # Server will redirect wpsl_stores-sitemap2.xml to\n+    # https://newspower.com.au/store-locator/ if it doesn't like\n+    # the country/netblock requesting the page.\n+    requires_proxy = True\n \n     def parse(self, response):\n         map_marker_js_blob = response.xpath('//script[contains(text(), \"var wpslMap_0 = \")]/text()').get()\n", "issue": "NewspowerAUSpider - can this be rewritten as a WPStoreLocator spider?\n```\r\nimport re\r\nfrom html import unescape\r\n\r\nfrom chompjs import parse_js_object\r\nfrom scrapy.spiders import SitemapSpider\r\n\r\nfrom locations.hours import OpeningHours\r\nfrom locations.items import Feature\r\n\r\n\r\nclass NewspowerAUSpider(SitemapSpider):\r\n    name = \"newspower_au\"\r\n    item_attributes = {\"brand\": \"Newspower\", \"brand_wikidata\": \"Q120670137\"}\r\n    allowed_domains = [\"newspower.com.au\"]\r\n    sitemap_urls = [\r\n        \"https://newspower.com.au/wpsl_stores-sitemap1.xml\",\r\n        \"https://newspower.com.au/wpsl_stores-sitemap2.xml\",\r\n    ]\r\n    sitemap_rules = [(\"/stores/\", \"parse\")]\r\n\r\n    def parse(self, response):\r\n        map_marker_js_blob = response.xpath('//script[contains(text(), \"var wpslMap_0 = \")]/text()').get()\r\n        map_marker_js_blob = map_marker_js_blob.split(\"var wpslMap_0 = \", 1)[1].split(\"]};\", 1)[0] + \"]}\"\r\n        map_marker_dict = parse_js_object(map_marker_js_blob)[\"locations\"][0]\r\n        properties = {\r\n            \"ref\": map_marker_dict[\"id\"],\r\n            \"name\": response.xpath('//div[@class=\"wpsl-locations-details\"]/span/strong/text()').get().strip(),\r\n            \"addr_full\": unescape(\r\n                re.sub(\r\n                    r\"\\s+\",\r\n                    \" \",\r\n                    \", \".join(filter(None, response.xpath('//div[@class=\"wpsl-location-address\"]//text()').getall())),\r\n                )\r\n            )\r\n            .replace(\" ,\", \",\")\r\n            .strip(),\r\n            \"street_address\": \", \".join(filter(None, [map_marker_dict[\"address\"], map_marker_dict[\"address2\"]])),\r\n            \"city\": map_marker_dict[\"city\"],\r\n            \"state\": map_marker_dict[\"state\"],\r\n            \"postcode\": map_marker_dict[\"zip\"],\r\n            \"lat\": map_marker_dict[\"lat\"],\r\n            \"lon\": map_marker_dict[\"lng\"],\r\n            \"phone\": response.xpath('//div[@class=\"wpsl-contact-details\"]//a[contains(@href, \"tel:\")]/@href').get(),\r\n            \"website\": response.url,\r\n            \"facebook\": response.xpath(\r\n                '//div[@class=\"entry-content\"]//a[contains(@href, \"https://www.facebook.com/\")]/@href'\r\n            ).get(),\r\n        }\r\n        if properties.get(\"phone\") and \"tel:\" in properties.get(\"phone\"):\r\n            properties[\"phone\"] = properties[\"phone\"].replace(\"tel:\", \"\")\r\n        hours_string = \" \".join(filter(None, response.xpath('//table[@class=\"wpsl-opening-hours\"]//text()').getall()))\r\n        properties[\"opening_hours\"] = OpeningHours()\r\n        properties[\"opening_hours\"].add_ranges_from_string(hours_string)\r\n        yield Feature(**properties)\r\n```\n", "before_files": [{"content": "import re\nfrom html import unescape\n\nfrom chompjs import parse_js_object\nfrom scrapy.spiders import SitemapSpider\n\nfrom locations.hours import OpeningHours\nfrom locations.items import Feature\n\n\nclass NewspowerAUSpider(SitemapSpider):\n    name = \"newspower_au\"\n    item_attributes = {\"brand\": \"Newspower\", \"brand_wikidata\": \"Q120670137\"}\n    allowed_domains = [\"newspower.com.au\"]\n    sitemap_urls = [\n        \"https://newspower.com.au/wpsl_stores-sitemap1.xml\",\n        \"https://newspower.com.au/wpsl_stores-sitemap2.xml\",\n    ]\n    sitemap_rules = [(\"/stores/\", \"parse\")]\n\n    def parse(self, response):\n        map_marker_js_blob = response.xpath('//script[contains(text(), \"var wpslMap_0 = \")]/text()').get()\n        map_marker_js_blob = map_marker_js_blob.split(\"var wpslMap_0 = \", 1)[1].split(\"]};\", 1)[0] + \"]}\"\n        map_marker_dict = parse_js_object(map_marker_js_blob)[\"locations\"][0]\n        properties = {\n            \"ref\": map_marker_dict[\"id\"],\n            \"name\": response.xpath('//div[@class=\"wpsl-locations-details\"]/span/strong/text()').get().strip(),\n            \"addr_full\": unescape(\n                re.sub(\n                    r\"\\s+\",\n                    \" \",\n                    \", \".join(filter(None, response.xpath('//div[@class=\"wpsl-location-address\"]//text()').getall())),\n                )\n            )\n            .replace(\" ,\", \",\")\n            .strip(),\n            \"street_address\": \", \".join(filter(None, [map_marker_dict[\"address\"], map_marker_dict[\"address2\"]])),\n            \"city\": map_marker_dict[\"city\"],\n            \"state\": map_marker_dict[\"state\"],\n            \"postcode\": map_marker_dict[\"zip\"],\n            \"lat\": map_marker_dict[\"lat\"],\n            \"lon\": map_marker_dict[\"lng\"],\n            \"phone\": response.xpath('//div[@class=\"wpsl-contact-details\"]//a[contains(@href, \"tel:\")]/@href').get(),\n            \"website\": response.url,\n            \"facebook\": response.xpath(\n                '//div[@class=\"entry-content\"]//a[contains(@href, \"https://www.facebook.com/\")]/@href'\n            ).get(),\n        }\n        if properties.get(\"phone\") and \"tel:\" in properties.get(\"phone\"):\n            properties[\"phone\"] = properties[\"phone\"].replace(\"tel:\", \"\")\n        hours_string = \" \".join(filter(None, response.xpath('//table[@class=\"wpsl-opening-hours\"]//text()').getall()))\n        properties[\"opening_hours\"] = OpeningHours()\n        properties[\"opening_hours\"].add_ranges_from_string(hours_string)\n        yield Feature(**properties)\n", "path": "locations/spiders/newspower_au.py"}], "after_files": [{"content": "import re\nfrom html import unescape\n\nfrom chompjs import parse_js_object\nfrom scrapy.spiders import SitemapSpider\n\nfrom locations.hours import OpeningHours\nfrom locations.items import Feature\n\n\nclass NewspowerAUSpider(SitemapSpider):\n    # Whilst WP Store Locator is used for this brand, it is set to\n    # return at most the 5 closest points to a provided search\n    # coordinate. There is an impractical number of search requests\n    # thus required to use the WP Store Locator store finder API.\n    # A Sitemap spider is used instead.\n    name = \"newspower_au\"\n    item_attributes = {\"brand\": \"Newspower\", \"brand_wikidata\": \"Q120670137\"}\n    allowed_domains = [\"newspower.com.au\"]\n    sitemap_urls = [\n        \"https://newspower.com.au/wpsl_stores-sitemap1.xml\",\n        \"https://newspower.com.au/wpsl_stores-sitemap2.xml\",\n    ]\n    sitemap_rules = [(r\"^https:\\/\\/newspower\\.com\\.au\\/stores/[^/]+\\/$\", \"parse\")]\n    # Server will redirect wpsl_stores-sitemap2.xml to\n    # https://newspower.com.au/store-locator/ if it doesn't like\n    # the country/netblock requesting the page.\n    requires_proxy = True\n\n    def parse(self, response):\n        map_marker_js_blob = response.xpath('//script[contains(text(), \"var wpslMap_0 = \")]/text()').get()\n        map_marker_js_blob = map_marker_js_blob.split(\"var wpslMap_0 = \", 1)[1].split(\"]};\", 1)[0] + \"]}\"\n        map_marker_dict = parse_js_object(map_marker_js_blob)[\"locations\"][0]\n        properties = {\n            \"ref\": map_marker_dict[\"id\"],\n            \"name\": response.xpath('//div[@class=\"wpsl-locations-details\"]/span/strong/text()').get().strip(),\n            \"addr_full\": unescape(\n                re.sub(\n                    r\"\\s+\",\n                    \" \",\n                    \", \".join(filter(None, response.xpath('//div[@class=\"wpsl-location-address\"]//text()').getall())),\n                )\n            )\n            .replace(\" ,\", \",\")\n            .strip(),\n            \"street_address\": \", \".join(filter(None, [map_marker_dict[\"address\"], map_marker_dict[\"address2\"]])),\n            \"city\": map_marker_dict[\"city\"],\n            \"state\": map_marker_dict[\"state\"],\n            \"postcode\": map_marker_dict[\"zip\"],\n            \"lat\": map_marker_dict[\"lat\"],\n            \"lon\": map_marker_dict[\"lng\"],\n            \"phone\": response.xpath('//div[@class=\"wpsl-contact-details\"]//a[contains(@href, \"tel:\")]/@href').get(),\n            \"website\": response.url,\n            \"facebook\": response.xpath(\n                '//div[@class=\"entry-content\"]//a[contains(@href, \"https://www.facebook.com/\")]/@href'\n            ).get(),\n        }\n        if properties.get(\"phone\") and \"tel:\" in properties.get(\"phone\"):\n            properties[\"phone\"] = properties[\"phone\"].replace(\"tel:\", \"\")\n        hours_string = \" \".join(filter(None, response.xpath('//table[@class=\"wpsl-opening-hours\"]//text()').getall()))\n        properties[\"opening_hours\"] = OpeningHours()\n        properties[\"opening_hours\"].add_ranges_from_string(hours_string)\n        yield Feature(**properties)\n", "path": "locations/spiders/newspower_au.py"}]}