We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
boots.py spider doesn't correctly pick up all opticians
The current test in boots.py to switch the brand tags for opticians is `properties["name"].startswith("Opticians - ")`:
https://github.com/alltheplaces/alltheplaces/blob/master/locations/spiders/boots.py#L73

But this is not general enough to catch all of them. The displayed name of some opticians branches only start with "Opticians " or "Opticians-". For example https://www.boots.com/stores/3730-tewkesbury-high-street-opticians-gl20-5jz and https://www.boots.com/stores/3947-camden-high-street-opticians-nw1-0lu

I think you could safely change the test to `properties["name"].startswith("Opticians")` but the code a few lines below to strip out the "Opticians" prefix would need to be more complicated.

--- 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/boots.py`
Content:
```
1 import scrapy
2 
3 from locations.items import GeojsonPointItem
4 
5 
6 class BootsSpider(scrapy.Spider):
7     name = "boots"
8     item_attributes = {"brand": "Boots", "brand_wikidata": "Q6123139"}
9     allowed_domains = ["www.boots.com", "www.boots.ie"]
10     download_delay = 0.5
11     start_urls = ["http://www.boots.com/store-a-z", "http://www.boots.ie/store-a-z"]
12 
13     def parse_hours(self, lis):
14         hours = []
15         for li in lis:
16             day = li.xpath(
17                 'normalize-space(./td[@class="store_hours_day"]/text())'
18             ).extract_first()
19             times = (
20                 li.xpath('normalize-space(./td[@class="store_hours_time"]/text())')
21                 .extract_first()
22                 .replace(" ", "")
23                 .replace("Closed-Closed", "off")
24             )
25             if times and day:
26                 hours.append(day[:2] + " " + times)
27 
28         return "; ".join(hours)
29 
30     def parse_stores(self, response):
31         addr_full = response.xpath(
32             '//section[@class="store_details_content rowContainer"]/dl[@class="store_info_list"][1]/dd[@class="store_info_list_item"]/text()'
33         ).extract()
34         address = ", ".join(map(str.strip, addr_full))
35         # Handle blank store pages e.g. https://www.boots.com/stores/2250-alnwick-paikes-street-ne66-1hx
36         if len(address) == 0:
37             return
38 
39         properties = {
40             "ref": response.xpath(
41                 'normalize-space(//input[@id="bootsStoreId"]/@value)'
42             ).extract_first(),
43             "name": response.xpath(
44                 'normalize-space(//input[@id="inputLocation"][@name="inputLocation"]/@value)'
45             ).extract_first(),
46             "postcode": response.xpath(
47                 'normalize-space(//input[@id="storePostcode"]/@value)'
48             ).extract_first(),
49             "addr_full": address,
50             "phone": response.xpath(
51                 '//section[@class="store_details_content rowContainer"]/dl[@class="store_info_list"][3]/dd[@class="store_info_list_item"]/a/text()'
52             ).extract_first(),
53             "country": response.xpath(
54                 'normalize-space(//input[@id="countryCode"][@name="countryCode"]/@value)'
55             ).extract_first(),
56             "website": response.url,
57             "lat": response.xpath(
58                 'normalize-space(//input[@id="lat"]/@value)'
59             ).extract_first(),
60             "lon": response.xpath(
61                 'normalize-space(//input[@id="lon"]/@value)'
62             ).extract_first(),
63         }
64 
65         hours = self.parse_hours(
66             response.xpath(
67                 '//div[@class="row store_all_opening_hours"]/div[1]/table[@class="store_opening_hours "]/tbody/tr'
68             )
69         )
70         if hours:
71             properties["opening_hours"] = hours
72 
73         if properties["name"].startswith("Opticians - "):
74             properties["brand"] = "Boots Opticians"
75             properties["brand_wikidata"] = "Q4944037"
76             properties["name"] = properties["name"][12:]
77 
78         yield GeojsonPointItem(**properties)
79 
80     def parse(self, response):
81         urls = response.xpath(
82             '//div[@class="brand_list_viewer"]/div[@class="column"]/ul/li/a/@href'
83         ).extract()
84         for path in urls:
85             yield scrapy.Request(response.urljoin(path), callback=self.parse_stores)
86
```
--- 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/locations/spiders/boots.py b/locations/spiders/boots.py
--- a/locations/spiders/boots.py
+++ b/locations/spiders/boots.py
@@ -70,10 +70,10 @@
         if hours:
             properties["opening_hours"] = hours
 
-        if properties["name"].startswith("Opticians - "):
+        if properties["name"].startswith("Opticians"):
             properties["brand"] = "Boots Opticians"
             properties["brand_wikidata"] = "Q4944037"
-            properties["name"] = properties["name"][12:]
+            properties["name"] = properties["name"].replace("Opticians", "").strip("- ")
 
         yield GeojsonPointItem(**properties)

{"golden_diff": "diff --git a/locations/spiders/boots.py b/locations/spiders/boots.py\n--- a/locations/spiders/boots.py\n+++ b/locations/spiders/boots.py\n@@ -70,10 +70,10 @@\n         if hours:\n             properties[\"opening_hours\"] = hours\n \n-        if properties[\"name\"].startswith(\"Opticians - \"):\n+        if properties[\"name\"].startswith(\"Opticians\"):\n             properties[\"brand\"] = \"Boots Opticians\"\n             properties[\"brand_wikidata\"] = \"Q4944037\"\n-            properties[\"name\"] = properties[\"name\"][12:]\n+            properties[\"name\"] = properties[\"name\"].replace(\"Opticians\", \"\").strip(\"- \")\n \n         yield GeojsonPointItem(**properties)\n", "issue": "boots.py spider doesn't correctly pick up all opticians\nThe current test in boots.py to switch the brand tags for opticians is `properties[\"name\"].startswith(\"Opticians - \")`:\r\nhttps://github.com/alltheplaces/alltheplaces/blob/master/locations/spiders/boots.py#L73\r\n\r\nBut this is not general enough to catch all of them. The displayed name of some opticians branches only start with \"Opticians \" or \"Opticians-\". For example https://www.boots.com/stores/3730-tewkesbury-high-street-opticians-gl20-5jz and https://www.boots.com/stores/3947-camden-high-street-opticians-nw1-0lu\r\n\r\nI think you could safely change the test to `properties[\"name\"].startswith(\"Opticians\")` but the code a few lines below to strip out the \"Opticians\" prefix would need to be more complicated.\n", "before_files": [{"content": "import scrapy\n\nfrom locations.items import GeojsonPointItem\n\n\nclass BootsSpider(scrapy.Spider):\n    name = \"boots\"\n    item_attributes = {\"brand\": \"Boots\", \"brand_wikidata\": \"Q6123139\"}\n    allowed_domains = [\"www.boots.com\", \"www.boots.ie\"]\n    download_delay = 0.5\n    start_urls = [\"http://www.boots.com/store-a-z\", \"http://www.boots.ie/store-a-z\"]\n\n    def parse_hours(self, lis):\n        hours = []\n        for li in lis:\n            day = li.xpath(\n                'normalize-space(./td[@class=\"store_hours_day\"]/text())'\n            ).extract_first()\n            times = (\n                li.xpath('normalize-space(./td[@class=\"store_hours_time\"]/text())')\n                .extract_first()\n                .replace(\" \", \"\")\n                .replace(\"Closed-Closed\", \"off\")\n            )\n            if times and day:\n                hours.append(day[:2] + \" \" + times)\n\n        return \"; \".join(hours)\n\n    def parse_stores(self, response):\n        addr_full = response.xpath(\n            '//section[@class=\"store_details_content rowContainer\"]/dl[@class=\"store_info_list\"][1]/dd[@class=\"store_info_list_item\"]/text()'\n        ).extract()\n        address = \", \".join(map(str.strip, addr_full))\n        # Handle blank store pages e.g. https://www.boots.com/stores/2250-alnwick-paikes-street-ne66-1hx\n        if len(address) == 0:\n            return\n\n        properties = {\n            \"ref\": response.xpath(\n                'normalize-space(//input[@id=\"bootsStoreId\"]/@value)'\n            ).extract_first(),\n            \"name\": response.xpath(\n                'normalize-space(//input[@id=\"inputLocation\"][@name=\"inputLocation\"]/@value)'\n            ).extract_first(),\n            \"postcode\": response.xpath(\n                'normalize-space(//input[@id=\"storePostcode\"]/@value)'\n            ).extract_first(),\n            \"addr_full\": address,\n            \"phone\": response.xpath(\n                '//section[@class=\"store_details_content rowContainer\"]/dl[@class=\"store_info_list\"][3]/dd[@class=\"store_info_list_item\"]/a/text()'\n            ).extract_first(),\n            \"country\": response.xpath(\n                'normalize-space(//input[@id=\"countryCode\"][@name=\"countryCode\"]/@value)'\n            ).extract_first(),\n            \"website\": response.url,\n            \"lat\": response.xpath(\n                'normalize-space(//input[@id=\"lat\"]/@value)'\n            ).extract_first(),\n            \"lon\": response.xpath(\n                'normalize-space(//input[@id=\"lon\"]/@value)'\n            ).extract_first(),\n        }\n\n        hours = self.parse_hours(\n            response.xpath(\n                '//div[@class=\"row store_all_opening_hours\"]/div[1]/table[@class=\"store_opening_hours \"]/tbody/tr'\n            )\n        )\n        if hours:\n            properties[\"opening_hours\"] = hours\n\n        if properties[\"name\"].startswith(\"Opticians - \"):\n            properties[\"brand\"] = \"Boots Opticians\"\n            properties[\"brand_wikidata\"] = \"Q4944037\"\n            properties[\"name\"] = properties[\"name\"][12:]\n\n        yield GeojsonPointItem(**properties)\n\n    def parse(self, response):\n        urls = response.xpath(\n            '//div[@class=\"brand_list_viewer\"]/div[@class=\"column\"]/ul/li/a/@href'\n        ).extract()\n        for path in urls:\n            yield scrapy.Request(response.urljoin(path), callback=self.parse_stores)\n", "path": "locations/spiders/boots.py"}], "after_files": [{"content": "import scrapy\n\nfrom locations.items import GeojsonPointItem\n\n\nclass BootsSpider(scrapy.Spider):\n    name = \"boots\"\n    item_attributes = {\"brand\": \"Boots\", \"brand_wikidata\": \"Q6123139\"}\n    allowed_domains = [\"www.boots.com\", \"www.boots.ie\"]\n    download_delay = 0.5\n    start_urls = [\"http://www.boots.com/store-a-z\", \"http://www.boots.ie/store-a-z\"]\n\n    def parse_hours(self, lis):\n        hours = []\n        for li in lis:\n            day = li.xpath(\n                'normalize-space(./td[@class=\"store_hours_day\"]/text())'\n            ).extract_first()\n            times = (\n                li.xpath('normalize-space(./td[@class=\"store_hours_time\"]/text())')\n                .extract_first()\n                .replace(\" \", \"\")\n                .replace(\"Closed-Closed\", \"off\")\n            )\n            if times and day:\n                hours.append(day[:2] + \" \" + times)\n\n        return \"; \".join(hours)\n\n    def parse_stores(self, response):\n        addr_full = response.xpath(\n            '//section[@class=\"store_details_content rowContainer\"]/dl[@class=\"store_info_list\"][1]/dd[@class=\"store_info_list_item\"]/text()'\n        ).extract()\n        address = \", \".join(map(str.strip, addr_full))\n        # Handle blank store pages e.g. https://www.boots.com/stores/2250-alnwick-paikes-street-ne66-1hx\n        if len(address) == 0:\n            return\n\n        properties = {\n            \"ref\": response.xpath(\n                'normalize-space(//input[@id=\"bootsStoreId\"]/@value)'\n            ).extract_first(),\n            \"name\": response.xpath(\n                'normalize-space(//input[@id=\"inputLocation\"][@name=\"inputLocation\"]/@value)'\n            ).extract_first(),\n            \"postcode\": response.xpath(\n                'normalize-space(//input[@id=\"storePostcode\"]/@value)'\n            ).extract_first(),\n            \"addr_full\": address,\n            \"phone\": response.xpath(\n                '//section[@class=\"store_details_content rowContainer\"]/dl[@class=\"store_info_list\"][3]/dd[@class=\"store_info_list_item\"]/a/text()'\n            ).extract_first(),\n            \"country\": response.xpath(\n                'normalize-space(//input[@id=\"countryCode\"][@name=\"countryCode\"]/@value)'\n            ).extract_first(),\n            \"website\": response.url,\n            \"lat\": response.xpath(\n                'normalize-space(//input[@id=\"lat\"]/@value)'\n            ).extract_first(),\n            \"lon\": response.xpath(\n                'normalize-space(//input[@id=\"lon\"]/@value)'\n            ).extract_first(),\n        }\n\n        hours = self.parse_hours(\n            response.xpath(\n                '//div[@class=\"row store_all_opening_hours\"]/div[1]/table[@class=\"store_opening_hours \"]/tbody/tr'\n            )\n        )\n        if hours:\n            properties[\"opening_hours\"] = hours\n\n        if properties[\"name\"].startswith(\"Opticians\"):\n            properties[\"brand\"] = \"Boots Opticians\"\n            properties[\"brand_wikidata\"] = \"Q4944037\"\n            properties[\"name\"] = properties[\"name\"].replace(\"Opticians\", \"\").strip(\"- \")\n\n        yield GeojsonPointItem(**properties)\n\n    def parse(self, response):\n        urls = response.xpath(\n            '//div[@class=\"brand_list_viewer\"]/div[@class=\"column\"]/ul/li/a/@href'\n        ).extract()\n        for path in urls:\n            yield scrapy.Request(response.urljoin(path), callback=self.parse_stores)\n", "path": "locations/spiders/boots.py"}]}