Repository: alltheplaces/alltheplaces

Issue:
Spider lowes is broken
During the global build at 2021-06-02-14-42-40, spider **lowes** failed with **0 features** and **0 errors**.

Here's [the log](https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/logs/lowes.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/output/lowes.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/output/lowes.geojson))


Discussion:

Resolving patch:
diff --git a/locations/spiders/lowes.py b/locations/spiders/lowes.py
--- a/locations/spiders/lowes.py
+++ b/locations/spiders/lowes.py
@@ -6,16 +6,23 @@
 from locations.hours import OpeningHours
 
 
-day_mapping = {'Monday': 'Mo', 'Tuesday': 'Tu', 'Wednesday': 'We', 'Thursday': 'Th', 'Friday': 'Fr', 'Saturday': 'Sa',
-               'Sunday': 'Su'}
+day_mapping = {
+    'Monday': 'Mo',
+    'Tuesday': 'Tu',
+    'Wednesday': 'We',
+    'Thursday': 'Th',
+    'Friday': 'Fr',
+    'Saturday': 'Sa',
+    'Sunday': 'Su',
+}
 
 
 class LowesSpider(scrapy.Spider):
     """"This spider scrapes Lowes retail store locations"""
     name = "lowes"
-    item_attributes = { 'brand': "Lowe's", 'brand_wikidata': "Q1373493" }
+    item_attributes = {'brand': "Lowe's", 'brand_wikidata': "Q1373493"}
     allowed_domains = ["lowes.com"]
-    start_urls = ('https://www.lowes.com/Lowes-Stores',)
+    start_urls = ('https://www.lowes.com/sitemap/store0.xml',)
     download_delay = 0.5
 
     custom_settings = {
@@ -59,14 +66,14 @@ def parse_store(self, response):
 
         state_texts = response.xpath('//span[@itemprop="addressRegion"]/text()').extract()
         properties = {
-            'lat': float(json_data['storeDetails']['lat']),
-            'lon': float(json_data['storeDetails']['long']),
-            'ref': ref,
-            'addr_full': response.xpath('normalize-space(//span[@itemprop="streetAddress"]/text())').extract_first(),
-            'city': response.xpath('normalize-space(//span[@itemprop="addressLocality"]/text())').extract_first(),
-            'state': " ".join(text.strip() for text in state_texts if text.strip()),
-            'postcode': response.xpath('normalize-space(//span[@itemprop="postalCode"]/text())').extract_first(),
-            'phone': response.xpath('normalize-space(//meta[@itemprop="telephone"]/@content)').extract_first(),
+            'lat': json_data['storeDetails']['lat'],
+            'lon': json_data['storeDetails']['long'],
+            'ref': json_data['storeDetails']['id'],
+            'addr_full': json_data['storeDetails']['address'],
+            'city': json_data['storeDetails']['city'],
+            'state': json_data['storeDetails']['state'],
+            'postcode': json_data['storeDetails']['zip'],
+            'phone': json_data['storeDetails']['phone'],
             'website': response.request.url,
             'opening_hours': self.parse_hours(store_hours),
             'extras': {
@@ -76,12 +83,9 @@ def parse_store(self, response):
 
         yield GeojsonPointItem(**properties)
 
-    def parse_state(self, response):
-        city_urls = response.xpath('//div[@class="v-spacing-small"]/a/@href').extract()
-        for path in city_urls:
-            yield scrapy.Request(response.urljoin(path), callback=self.parse_store)
-
     def parse(self, response):
-        urls = response.xpath('//div[@id="mainContent"]//li[@role="listitem"]/a/@href').extract()
-        for path in urls:
-            yield scrapy.Request(response.urljoin(path), callback=self.parse_state)
+        response.selector.remove_namespaces()
+        urls = response.xpath('//url/loc/text()').extract()
+
+        for url in urls:
+            yield scrapy.Request(url, callback=self.parse_store)


Test patch: