prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Spider sallybeauty is broken
  During the global build at 2021-05-26-14-42-23, spider **sallybeauty** failed with **2712 features** and **5 errors**.
  
  Here's [the log](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/logs/sallybeauty.log) and [the output](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/sallybeauty.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/sallybeauty.geojson))
  
  --- 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/sallybeauty.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 import scrapy
  3 from locations.items import GeojsonPointItem
  4 from urllib.parse import urlencode
  5 import json
  6 import csv
  7 from locations.hours import OpeningHours
  8 from scrapy.selector import Selector
  9 
  10 
  11 class SallySpider(scrapy.Spider):
  12     name = "sallybeauty"
  13     item_attributes = { 'brand': "Sally Beauty" }
  14     allowed_domains = ["sallybeauty.com"]
  15 
  16     def start_requests(self):
  17         base_url = "https://www.sallybeauty.com/on/demandware.store/Sites-SA-Site/default/Stores-FindStores?"
  18 
  19         point_files = [
  20             './locations/searchable_points/us_centroids_100mile_radius.csv',
  21             './locations/searchable_points/ca_centroids_100mile_radius.csv'
  22         ]
  23 
  24         params = {
  25             "showmap": "true",
  26             "radius": "100",
  27         }
  28 
  29         for point_file in point_files:
  30             with open(point_file) as points:
  31                 next(points)
  32                 for point in points:
  33                     _, lat, lon = point.strip().split(',')
  34                     params.update({"lat": lat, "long": lon})
  35                     yield scrapy.Request(url=base_url + urlencode(params))
  36 
  37     def parse_hours(self, hours):
  38         hrs = Selector(text=hours)
  39         days = hrs.xpath('//div[@class="store-hours-day"]/text()').extract()
  40         hours = hrs.xpath('//div[@class="store-hours-day"]/span/text()').extract()
  41 
  42         opening_hours = OpeningHours()
  43 
  44         for d, h in zip(days, hours):
  45             try:
  46                 day = d.strip(': ')
  47                 open_time, close_time = h.split(' - ')
  48                 open_time = open_time.lstrip('0')
  49                 opening_hours.add_range(day=day[:2],
  50                                         open_time=open_time,
  51                                         close_time=close_time,
  52                                         time_format="%I:%M %p")
  53             except:
  54                 continue
  55 
  56         return opening_hours.as_opening_hours()
  57 
  58     def parse(self, response):
  59         jdata = json.loads(response.body_as_unicode())
  60 
  61         for row in jdata.get('stores',[]):
  62 
  63             properties = {
  64                 'ref': row["ID"],
  65                 'name': row["name"],
  66                 'addr_full': " ".join([row["address1"], row.get("address2", "") or ""]).strip(),
  67                 'city': row["city"],
  68                 'postcode': row["postalCode"],
  69                 'lat': row["latitude"],
  70                 'lon': row["longitude"],
  71                 'phone': row["phone"],
  72                 'state': row["stateCode"],
  73             }
  74 
  75             hours = self.parse_hours(row["storeHours"])
  76             if hours:
  77                 properties['opening_hours'] = hours
  78 
  79             yield GeojsonPointItem(**properties)
  80
  ```
  --- 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/sallybeauty.py b/locations/spiders/sallybeauty.py
  --- a/locations/spiders/sallybeauty.py
  +++ b/locations/spiders/sallybeauty.py
  @@ -58,7 +58,7 @@
       def parse(self, response):
           jdata = json.loads(response.body_as_unicode())
   
  -        for row in jdata.get('stores',[]):
  +        for row in jdata.get('stores', []):
   
               properties = {
                   'ref': row["ID"],
  @@ -72,8 +72,11 @@
                   'state': row["stateCode"],
               }
   
  -            hours = self.parse_hours(row["storeHours"])
  -            if hours:
  -                properties['opening_hours'] = hours
  +            store_hours = row.get("storeHours")
  +            if store_hours:
  +                hours = self.parse_hours(store_hours)
  +
  +                if hours:
  +                    properties['opening_hours'] = hours
   
               yield GeojsonPointItem(**properties)
  
verification_info: {"golden_diff": "diff --git a/locations/spiders/sallybeauty.py b/locations/spiders/sallybeauty.py\n--- a/locations/spiders/sallybeauty.py\n+++ b/locations/spiders/sallybeauty.py\n@@ -58,7 +58,7 @@\n     def parse(self, response):\n         jdata = json.loads(response.body_as_unicode())\n \n-        for row in jdata.get('stores',[]):\n+        for row in jdata.get('stores', []):\n \n             properties = {\n                 'ref': row[\"ID\"],\n@@ -72,8 +72,11 @@\n                 'state': row[\"stateCode\"],\n             }\n \n-            hours = self.parse_hours(row[\"storeHours\"])\n-            if hours:\n-                properties['opening_hours'] = hours\n+            store_hours = row.get(\"storeHours\")\n+            if store_hours:\n+                hours = self.parse_hours(store_hours)\n+\n+                if hours:\n+                    properties['opening_hours'] = hours\n \n             yield GeojsonPointItem(**properties)\n", "issue": "Spider sallybeauty is broken\nDuring the global build at 2021-05-26-14-42-23, spider **sallybeauty** failed with **2712 features** and **5 errors**.\n\nHere's [the log](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/logs/sallybeauty.log) and [the output](https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/sallybeauty.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-05-26-14-42-23/output/sallybeauty.geojson))\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nimport scrapy\nfrom locations.items import GeojsonPointItem\nfrom urllib.parse import urlencode\nimport json\nimport csv\nfrom locations.hours import OpeningHours\nfrom scrapy.selector import Selector\n\n\nclass SallySpider(scrapy.Spider):\n    name = \"sallybeauty\"\n    item_attributes = { 'brand': \"Sally Beauty\" }\n    allowed_domains = [\"sallybeauty.com\"]\n\n    def start_requests(self):\n        base_url = \"https://www.sallybeauty.com/on/demandware.store/Sites-SA-Site/default/Stores-FindStores?\"\n\n        point_files = [\n            './locations/searchable_points/us_centroids_100mile_radius.csv',\n            './locations/searchable_points/ca_centroids_100mile_radius.csv'\n        ]\n\n        params = {\n            \"showmap\": \"true\",\n            \"radius\": \"100\",\n        }\n\n        for point_file in point_files:\n            with open(point_file) as points:\n                next(points)\n                for point in points:\n                    _, lat, lon = point.strip().split(',')\n                    params.update({\"lat\": lat, \"long\": lon})\n                    yield scrapy.Request(url=base_url + urlencode(params))\n\n    def parse_hours(self, hours):\n        hrs = Selector(text=hours)\n        days = hrs.xpath('//div[@class=\"store-hours-day\"]/text()').extract()\n        hours = hrs.xpath('//div[@class=\"store-hours-day\"]/span/text()').extract()\n\n        opening_hours = OpeningHours()\n\n        for d, h in zip(days, hours):\n            try:\n                day = d.strip(': ')\n                open_time, close_time = h.split(' - ')\n                open_time = open_time.lstrip('0')\n                opening_hours.add_range(day=day[:2],\n                                        open_time=open_time,\n                                        close_time=close_time,\n                                        time_format=\"%I:%M %p\")\n            except:\n                continue\n\n        return opening_hours.as_opening_hours()\n\n    def parse(self, response):\n        jdata = json.loads(response.body_as_unicode())\n\n        for row in jdata.get('stores',[]):\n\n            properties = {\n                'ref': row[\"ID\"],\n                'name': row[\"name\"],\n                'addr_full': \" \".join([row[\"address1\"], row.get(\"address2\", \"\") or \"\"]).strip(),\n                'city': row[\"city\"],\n                'postcode': row[\"postalCode\"],\n                'lat': row[\"latitude\"],\n                'lon': row[\"longitude\"],\n                'phone': row[\"phone\"],\n                'state': row[\"stateCode\"],\n            }\n\n            hours = self.parse_hours(row[\"storeHours\"])\n            if hours:\n                properties['opening_hours'] = hours\n\n            yield GeojsonPointItem(**properties)\n", "path": "locations/spiders/sallybeauty.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nimport scrapy\nfrom locations.items import GeojsonPointItem\nfrom urllib.parse import urlencode\nimport json\nimport csv\nfrom locations.hours import OpeningHours\nfrom scrapy.selector import Selector\n\n\nclass SallySpider(scrapy.Spider):\n    name = \"sallybeauty\"\n    item_attributes = { 'brand': \"Sally Beauty\" }\n    allowed_domains = [\"sallybeauty.com\"]\n\n    def start_requests(self):\n        base_url = \"https://www.sallybeauty.com/on/demandware.store/Sites-SA-Site/default/Stores-FindStores?\"\n\n        point_files = [\n            './locations/searchable_points/us_centroids_100mile_radius.csv',\n            './locations/searchable_points/ca_centroids_100mile_radius.csv'\n        ]\n\n        params = {\n            \"showmap\": \"true\",\n            \"radius\": \"100\",\n        }\n\n        for point_file in point_files:\n            with open(point_file) as points:\n                next(points)\n                for point in points:\n                    _, lat, lon = point.strip().split(',')\n                    params.update({\"lat\": lat, \"long\": lon})\n                    yield scrapy.Request(url=base_url + urlencode(params))\n\n    def parse_hours(self, hours):\n        hrs = Selector(text=hours)\n        days = hrs.xpath('//div[@class=\"store-hours-day\"]/text()').extract()\n        hours = hrs.xpath('//div[@class=\"store-hours-day\"]/span/text()').extract()\n\n        opening_hours = OpeningHours()\n\n        for d, h in zip(days, hours):\n            try:\n                day = d.strip(': ')\n                open_time, close_time = h.split(' - ')\n                open_time = open_time.lstrip('0')\n                opening_hours.add_range(day=day[:2],\n                                        open_time=open_time,\n                                        close_time=close_time,\n                                        time_format=\"%I:%M %p\")\n            except:\n                continue\n\n        return opening_hours.as_opening_hours()\n\n    def parse(self, response):\n        jdata = json.loads(response.body_as_unicode())\n\n        for row in jdata.get('stores', []):\n\n            properties = {\n                'ref': row[\"ID\"],\n                'name': row[\"name\"],\n                'addr_full': \" \".join([row[\"address1\"], row.get(\"address2\", \"\") or \"\"]).strip(),\n                'city': row[\"city\"],\n                'postcode': row[\"postalCode\"],\n                'lat': row[\"latitude\"],\n                'lon': row[\"longitude\"],\n                'phone': row[\"phone\"],\n                'state': row[\"stateCode\"],\n            }\n\n            store_hours = row.get(\"storeHours\")\n            if store_hours:\n                hours = self.parse_hours(store_hours)\n\n                if hours:\n                    properties['opening_hours'] = hours\n\n            yield GeojsonPointItem(**properties)\n", "path": "locations/spiders/sallybeauty.py"}]}