We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
IHG hotels scraper missing IHG Army Hotels
Missing this additional source of hotel listings:
https://www.ihg.com/armyhotels/

--- 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/ihg_hotels.py`
Content:
```
1 import json
2 import re
3 import scrapy
4 
5 from locations.items import GeojsonPointItem
6 
7 
8 class IHGHotels(scrapy.Spider):
9 
10     name = "ihg_hotels"
11     # allowed_domains = ["ihg.com"]  # the Kimpton hotels each have their own domains
12     download_delay = 0.5
13 
14     start_urls = (
15         'https://www.ihg.com/holidayinn/destinations/us/en/explore',
16     )
17 
18     def parse_hotel(self, response):
19         if 'hoteldetail' not in response.url:
20             # got redirected back to search page
21             return
22 
23         street_address = " ".join(response.xpath('//span[@itemprop="streetAddress"]/p/text()').extract())
24         if not street_address:
25             street_address = response.xpath('//span[@itemprop="streetAddress"]/text()').extract_first()
26 
27         city = response.xpath('//span[@itemprop="addressLocality"]/text()').extract_first()
28         state = response.xpath('//span[@itemprop="addressRegion"]/text()').extract_first()
29 
30         properties = {
31             'ref': "_".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),
32             'name': response.xpath('//meta[@property="og:title"]/@content').extract_first(),
33             'addr_full': street_address.replace(u'\u00a0', ' ').strip(', ') if street_address else None,
34             'city': city.replace(u'\u00a0', ' ').strip(', ') if city else None,
35             'state': state.replace(u'\u00a0', ' ') if state else None,
36             'postcode': response.xpath('//span[@itemprop="postalCode"]/text()').extract_first(),
37             'country': response.xpath('//span[@itemprop="addressCountry"]/text()').extract_first(),
38             'phone': (response.xpath('//span[@itemprop="telephone"]/text()').extract_first() or '').strip('| '),
39             'lat': float(response.xpath('//meta[@property="place:location:latitude"]/@content').extract_first()),
40             'lon': float(response.xpath('//meta[@property="place:location:longitude"]/@content').extract_first()),
41             'website': response.url,
42         }
43 
44         yield GeojsonPointItem(**properties)
45 
46     def parse_kimpton(self, response):
47         url = response.xpath('//a[contains(text(), "VISIT HOTEL WEBSITE")]/@href').extract_first()
48         properties = {
49             'ref': "_".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),
50             'lat': float(response.xpath('//meta[@property="place:location:latitude"]/@content').extract_first()),
51             'lon': float(response.xpath('//meta[@property="place:location:longitude"]/@content').extract_first()),
52         }
53         if not url:  # "opening soon" hotels just have teaser pages
54             return
55         url = url.split('?')[0]  # remove querystring
56         yield scrapy.Request(url, callback=self.parse_kimpton_data, meta={"properties": properties})
57 
58     def parse_kimpton_data(self, response):
59         properties = response.meta["properties"]
60         script = response.xpath('//script[@type="application/ld+json"]/text()').extract_first()
61         if script:
62             data = json.loads(script)
63         else:
64             data = {}
65         if 'name' in data:
66             properties.update({
67                 'name': data["name"],
68                 'addr_full': data["address"]["streetAddress"],
69                 'city': data["address"]["addressLocality"],
70                 'state': data["address"].get("addressRegion"),
71                 'postcode': data["address"]["postalCode"],
72                 'country': data["address"].get("addressCountry"),
73                 'phone': data.get("telephone"),
74                 'website': data["url"]
75             })
76 
77         else:
78             street_address = " ".join(response.xpath('//span[@itemprop="streetAddress"]/p/text()').extract())
79             if not street_address:
80                 street_address = response.xpath('//span[@itemprop="streetAddress"]/text()').extract_first()
81 
82             city = response.xpath('//span[@itemprop="addressLocality"]/text()').extract_first()
83             state = response.xpath('//span[@itemprop="addressRegion"]/text()').extract_first()
84 
85             properties.update({
86                 'name': response.xpath('//meta[@property="og:title"]/@content').extract_first(),
87                 'addr_full': street_address.replace(u'\u00a0', ' ').strip(', ') if street_address else None,
88                 'city': city.replace(u'\u00a0', ' ').strip(', ') if city else None,
89                 'state': state.replace(u'\u00a0', ' ') if state else None,
90                 'postcode': response.xpath('//span[@itemprop="postalCode"]/text()').extract_first(),
91                 'country': response.xpath('//span[@itemprop="addressCountry"]/text()').extract_first(),
92                 'phone': (response.xpath('//span[@itemprop="telephone"]/text()').extract_first() or '').strip('| '),
93                 'website': response.url,
94             })
95 
96         yield GeojsonPointItem(**properties)
97 
98     def parse_regent(self, response):
99         data = json.loads(response.xpath('//script[@type="application/ld+json"]/text()').extract_first())
100 
101         properties = {
102             'ref': "_".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),
103             'name': data["name"],
104             'addr_full': data["address"]["streetAddress"],
105             'city': data["address"]["addressLocality"],
106             'state': data["address"].get("addressRegion"),
107             'postcode': data["address"]["postalCode"],
108             'country': data["address"]["addressCountry"],
109             'phone': data["telephone"],
110             'lat': float(data["geo"]["latitude"]),
111             'lon': float(data["geo"]["longitude"]),
112             'website': response.url,
113         }
114 
115         yield GeojsonPointItem(**properties)
116 
117     def parse_crowne_plaza(self, response):
118         address = response.xpath('//a[@class="hotel-home"]/text()').extract_first().strip()
119 
120         address_parts = address.split('|')
121 
122         if len(address_parts) == 4:  # international addresses
123             addr_city, postcode, country, _ = address_parts
124             state = ''
125         else:  # us addresses
126             addr_city, state, postcode, country, _ = address_parts
127 
128         street_address = ",".join(addr_city.split(',')[0:-1])
129         city = addr_city.split(',')[-1]
130 
131         properties = {
132             'ref': "_".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),
133             'name': response.xpath('//meta[@property="og:title"]/@content').extract_first(),
134             'addr_full': street_address.strip(),
135             'city': city.strip(),
136             'state': state.strip(),
137             'postcode': postcode.strip(),
138             'country': country.strip(),
139             'phone': response.xpath('//div[@class="new-hinfo-address"]/p/a[2]/text()').extract_first(),
140             'lat': float(response.xpath('//meta[@property="place:location:latitude"]/@content').extract_first()),
141             'lon': float(response.xpath('//meta[@property="place:location:longitude"]/@content').extract_first()),
142             'website': response.url,
143         }
144 
145         yield GeojsonPointItem(**properties)
146 
147     def parse_candlewood_staybridge(self, response):
148         if 'hoteldetail' not in response.url:
149             # got redirected back to search page
150             return
151 
152         street_address = " ".join(response.xpath('//span[@itemprop="streetAddress"]/p/text()').extract())
153         if not street_address:
154             street_address = response.xpath('//span[@itemprop="streetAddress"]/text()').extract_first()
155 
156         region = response.xpath('//span[@itemprop="addressRegion"]/text()').extract_first().replace(u'\u00a0',' ')
157 
158         match = re.search(r'([a-z]+)\s(\d+)\s(.*)', region, re.IGNORECASE)
159         if match:
160             state, postcode, country = match.groups()
161         else:
162             state, postcode, country = None, None, region.strip()
163 
164         properties = {
165             'ref': "_".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),
166             'name': response.xpath('//meta[@property="og:title"]/@content').extract_first(),
167             'addr_full': street_address.replace(u'\u00a0', ' ').strip(', '),
168             'city': response.xpath('//span[@itemprop="addressLocality"]/text()').extract_first().replace(u'\u00a0', ' ').strip(', '),
169             'state': state,
170             'postcode': postcode,
171             'country': country,
172             'phone': response.xpath('//div[@class="booking"]/a/text()').extract_first(),
173             'lat': float(response.xpath('//meta[@property="place:location:latitude"]/@content').extract_first()),
174             'lon': float(response.xpath('//meta[@property="place:location:longitude"]/@content').extract_first()),
175             'website': response.url,
176         }
177 
178         yield GeojsonPointItem(**properties)
179 
180     def parse(self, response):
181 
182         hotel_parsers = {
183             'holidayinn': self.parse_hotel,
184             'crowneplaza': self.parse_crowne_plaza,
185             'holidayinnexpress': self.parse_hotel,
186             'hotelindigo': self.parse_hotel,
187             'candlewood': self.parse_candlewood_staybridge,
188             'staybridge': self.parse_candlewood_staybridge,
189             'holidayinnresorts': self.parse_hotel,
190             'intercontinental': self.parse_hotel,
191             'regent': self.parse_regent,
192             'hotels': self.parse_hotel,  # vocos
193             'kimptonhotels': self.parse_kimpton,
194             'holidayinnclubvacations': self.parse_hotel,
195             'evenhotels': self.parse_hotel,
196             'avidhotels': self.parse_hotel,
197             'hualuxe': self.parse_hotel
198         }
199 
200         hotel_urls = response.xpath('//div[@class="hotelList"]//div[contains(@class, "hotelItem")]//a[contains(@class, "hotel-name")]/@href').extract()
201         if hotel_urls:
202             for url in hotel_urls:
203                 hotel_type = re.search(r'ihg.com/(.*?)/', response.urljoin(url), re.IGNORECASE).group(1)
204 
205                 yield scrapy.Request(response.urljoin(url),
206                                      callback=hotel_parsers[hotel_type])
207 
208         else:
209             urls = response.xpath('//li[@class="listingItem"]/a/@href').extract()
210             for url in urls:
211                 yield scrapy.Request(response.urljoin(url))
212
```
--- 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/ihg_hotels.py b/locations/spiders/ihg_hotels.py
--- a/locations/spiders/ihg_hotels.py
+++ b/locations/spiders/ihg_hotels.py
@@ -13,6 +13,7 @@
 
     start_urls = (
         'https://www.ihg.com/holidayinn/destinations/us/en/explore',
+        'https://www.ihg.com/armyhotels/hotels/us/en/installations',
     )
 
     def parse_hotel(self, response):
@@ -32,7 +33,7 @@
             'name': response.xpath('//meta[@property="og:title"]/@content').extract_first(),
             'addr_full': street_address.replace(u'\u00a0', ' ').strip(', ') if street_address else None,
             'city': city.replace(u'\u00a0', ' ').strip(', ') if city else None,
-            'state': state.replace(u'\u00a0', ' ') if state else None,
+            'state': state.replace(u'\u00a0', ' ').strip(', ') if state else None,
             'postcode': response.xpath('//span[@itemprop="postalCode"]/text()').extract_first(),
             'country': response.xpath('//span[@itemprop="addressCountry"]/text()').extract_first(),
             'phone': (response.xpath('//span[@itemprop="telephone"]/text()').extract_first() or '').strip('| '),
@@ -177,6 +178,23 @@
 
         yield GeojsonPointItem(**properties)
 
+    def parse_army_hotel(self, response):
+        properties = {
+            'ref': "_".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),
+            'name': response.xpath('//meta[@property="og:title"]/@content').extract_first(),
+            'addr_full': response.xpath('//meta[@property="business:contact_data:street_address"]/@content').extract_first(),
+            'city': response.xpath('//meta[@property="business:contact_data:locality"]/@content').extract_first(),
+            'state': response.xpath('//meta[@property="business:contact_data:region"]/@content').extract_first(),
+            'postcode': response.xpath('//meta[@property="business:contact_data:postal_code"]/@content').extract_first(),
+            'country': response.xpath('//meta[@property="business:contact_data:country_name"]/@content').extract_first(),
+            'phone': (response.xpath('//span[@title="Hotel Front Desk:"]/span/text()').extract_first() or "").strip(),
+            'lat': float(response.xpath('//meta[@property="place:location:latitude"]/@content').extract_first()),
+            'lon': float(response.xpath('//meta[@property="place:location:longitude"]/@content').extract_first()),
+            'website': response.url,
+        }
+
+        yield GeojsonPointItem(**properties)
+
     def parse(self, response):
 
         hotel_parsers = {
@@ -194,10 +212,15 @@
             'holidayinnclubvacations': self.parse_hotel,
             'evenhotels': self.parse_hotel,
             'avidhotels': self.parse_hotel,
-            'hualuxe': self.parse_hotel
+            'hualuxe': self.parse_hotel,
+            'armyhotels': self.parse_army_hotel
         }
 
         hotel_urls = response.xpath('//div[@class="hotelList"]//div[contains(@class, "hotelItem")]//a[contains(@class, "hotel-name")]/@href').extract()
+
+        if 'armyhotels' in response.url:
+            hotel_urls = response.xpath('//div[@id="hotelListWrap"]//a/@href').extract()
+
         if hotel_urls:
             for url in hotel_urls:
                 hotel_type = re.search(r'ihg.com/(.*?)/', response.urljoin(url), re.IGNORECASE).group(1)

{"golden_diff": "diff --git a/locations/spiders/ihg_hotels.py b/locations/spiders/ihg_hotels.py\n--- a/locations/spiders/ihg_hotels.py\n+++ b/locations/spiders/ihg_hotels.py\n@@ -13,6 +13,7 @@\n \n     start_urls = (\n         'https://www.ihg.com/holidayinn/destinations/us/en/explore',\n+        'https://www.ihg.com/armyhotels/hotels/us/en/installations',\n     )\n \n     def parse_hotel(self, response):\n@@ -32,7 +33,7 @@\n             'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n             'addr_full': street_address.replace(u'\\u00a0', ' ').strip(', ') if street_address else None,\n             'city': city.replace(u'\\u00a0', ' ').strip(', ') if city else None,\n-            'state': state.replace(u'\\u00a0', ' ') if state else None,\n+            'state': state.replace(u'\\u00a0', ' ').strip(', ') if state else None,\n             'postcode': response.xpath('//span[@itemprop=\"postalCode\"]/text()').extract_first(),\n             'country': response.xpath('//span[@itemprop=\"addressCountry\"]/text()').extract_first(),\n             'phone': (response.xpath('//span[@itemprop=\"telephone\"]/text()').extract_first() or '').strip('| '),\n@@ -177,6 +178,23 @@\n \n         yield GeojsonPointItem(**properties)\n \n+    def parse_army_hotel(self, response):\n+        properties = {\n+            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n+            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n+            'addr_full': response.xpath('//meta[@property=\"business:contact_data:street_address\"]/@content').extract_first(),\n+            'city': response.xpath('//meta[@property=\"business:contact_data:locality\"]/@content').extract_first(),\n+            'state': response.xpath('//meta[@property=\"business:contact_data:region\"]/@content').extract_first(),\n+            'postcode': response.xpath('//meta[@property=\"business:contact_data:postal_code\"]/@content').extract_first(),\n+            'country': response.xpath('//meta[@property=\"business:contact_data:country_name\"]/@content').extract_first(),\n+            'phone': (response.xpath('//span[@title=\"Hotel Front Desk:\"]/span/text()').extract_first() or \"\").strip(),\n+            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n+            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n+            'website': response.url,\n+        }\n+\n+        yield GeojsonPointItem(**properties)\n+\n     def parse(self, response):\n \n         hotel_parsers = {\n@@ -194,10 +212,15 @@\n             'holidayinnclubvacations': self.parse_hotel,\n             'evenhotels': self.parse_hotel,\n             'avidhotels': self.parse_hotel,\n-            'hualuxe': self.parse_hotel\n+            'hualuxe': self.parse_hotel,\n+            'armyhotels': self.parse_army_hotel\n         }\n \n         hotel_urls = response.xpath('//div[@class=\"hotelList\"]//div[contains(@class, \"hotelItem\")]//a[contains(@class, \"hotel-name\")]/@href').extract()\n+\n+        if 'armyhotels' in response.url:\n+            hotel_urls = response.xpath('//div[@id=\"hotelListWrap\"]//a/@href').extract()\n+\n         if hotel_urls:\n             for url in hotel_urls:\n                 hotel_type = re.search(r'ihg.com/(.*?)/', response.urljoin(url), re.IGNORECASE).group(1)\n", "issue": "IHG hotels scraper missing IHG Army Hotels\nMissing this additional source of hotel listings:\r\nhttps://www.ihg.com/armyhotels/\n", "before_files": [{"content": "import json\nimport re\nimport scrapy\n\nfrom locations.items import GeojsonPointItem\n\n\nclass IHGHotels(scrapy.Spider):\n\n    name = \"ihg_hotels\"\n    # allowed_domains = [\"ihg.com\"]  # the Kimpton hotels each have their own domains\n    download_delay = 0.5\n\n    start_urls = (\n        'https://www.ihg.com/holidayinn/destinations/us/en/explore',\n    )\n\n    def parse_hotel(self, response):\n        if 'hoteldetail' not in response.url:\n            # got redirected back to search page\n            return\n\n        street_address = \" \".join(response.xpath('//span[@itemprop=\"streetAddress\"]/p/text()').extract())\n        if not street_address:\n            street_address = response.xpath('//span[@itemprop=\"streetAddress\"]/text()').extract_first()\n\n        city = response.xpath('//span[@itemprop=\"addressLocality\"]/text()').extract_first()\n        state = response.xpath('//span[@itemprop=\"addressRegion\"]/text()').extract_first()\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n            'addr_full': street_address.replace(u'\\u00a0', ' ').strip(', ') if street_address else None,\n            'city': city.replace(u'\\u00a0', ' ').strip(', ') if city else None,\n            'state': state.replace(u'\\u00a0', ' ') if state else None,\n            'postcode': response.xpath('//span[@itemprop=\"postalCode\"]/text()').extract_first(),\n            'country': response.xpath('//span[@itemprop=\"addressCountry\"]/text()').extract_first(),\n            'phone': (response.xpath('//span[@itemprop=\"telephone\"]/text()').extract_first() or '').strip('| '),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_kimpton(self, response):\n        url = response.xpath('//a[contains(text(), \"VISIT HOTEL WEBSITE\")]/@href').extract_first()\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n        }\n        if not url:  # \"opening soon\" hotels just have teaser pages\n            return\n        url = url.split('?')[0]  # remove querystring\n        yield scrapy.Request(url, callback=self.parse_kimpton_data, meta={\"properties\": properties})\n\n    def parse_kimpton_data(self, response):\n        properties = response.meta[\"properties\"]\n        script = response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first()\n        if script:\n            data = json.loads(script)\n        else:\n            data = {}\n        if 'name' in data:\n            properties.update({\n                'name': data[\"name\"],\n                'addr_full': data[\"address\"][\"streetAddress\"],\n                'city': data[\"address\"][\"addressLocality\"],\n                'state': data[\"address\"].get(\"addressRegion\"),\n                'postcode': data[\"address\"][\"postalCode\"],\n                'country': data[\"address\"].get(\"addressCountry\"),\n                'phone': data.get(\"telephone\"),\n                'website': data[\"url\"]\n            })\n\n        else:\n            street_address = \" \".join(response.xpath('//span[@itemprop=\"streetAddress\"]/p/text()').extract())\n            if not street_address:\n                street_address = response.xpath('//span[@itemprop=\"streetAddress\"]/text()').extract_first()\n\n            city = response.xpath('//span[@itemprop=\"addressLocality\"]/text()').extract_first()\n            state = response.xpath('//span[@itemprop=\"addressRegion\"]/text()').extract_first()\n\n            properties.update({\n                'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n                'addr_full': street_address.replace(u'\\u00a0', ' ').strip(', ') if street_address else None,\n                'city': city.replace(u'\\u00a0', ' ').strip(', ') if city else None,\n                'state': state.replace(u'\\u00a0', ' ') if state else None,\n                'postcode': response.xpath('//span[@itemprop=\"postalCode\"]/text()').extract_first(),\n                'country': response.xpath('//span[@itemprop=\"addressCountry\"]/text()').extract_first(),\n                'phone': (response.xpath('//span[@itemprop=\"telephone\"]/text()').extract_first() or '').strip('| '),\n                'website': response.url,\n            })\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_regent(self, response):\n        data = json.loads(response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first())\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': data[\"name\"],\n            'addr_full': data[\"address\"][\"streetAddress\"],\n            'city': data[\"address\"][\"addressLocality\"],\n            'state': data[\"address\"].get(\"addressRegion\"),\n            'postcode': data[\"address\"][\"postalCode\"],\n            'country': data[\"address\"][\"addressCountry\"],\n            'phone': data[\"telephone\"],\n            'lat': float(data[\"geo\"][\"latitude\"]),\n            'lon': float(data[\"geo\"][\"longitude\"]),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_crowne_plaza(self, response):\n        address = response.xpath('//a[@class=\"hotel-home\"]/text()').extract_first().strip()\n\n        address_parts = address.split('|')\n\n        if len(address_parts) == 4:  # international addresses\n            addr_city, postcode, country, _ = address_parts\n            state = ''\n        else:  # us addresses\n            addr_city, state, postcode, country, _ = address_parts\n\n        street_address = \",\".join(addr_city.split(',')[0:-1])\n        city = addr_city.split(',')[-1]\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n            'addr_full': street_address.strip(),\n            'city': city.strip(),\n            'state': state.strip(),\n            'postcode': postcode.strip(),\n            'country': country.strip(),\n            'phone': response.xpath('//div[@class=\"new-hinfo-address\"]/p/a[2]/text()').extract_first(),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_candlewood_staybridge(self, response):\n        if 'hoteldetail' not in response.url:\n            # got redirected back to search page\n            return\n\n        street_address = \" \".join(response.xpath('//span[@itemprop=\"streetAddress\"]/p/text()').extract())\n        if not street_address:\n            street_address = response.xpath('//span[@itemprop=\"streetAddress\"]/text()').extract_first()\n\n        region = response.xpath('//span[@itemprop=\"addressRegion\"]/text()').extract_first().replace(u'\\u00a0',' ')\n\n        match = re.search(r'([a-z]+)\\s(\\d+)\\s(.*)', region, re.IGNORECASE)\n        if match:\n            state, postcode, country = match.groups()\n        else:\n            state, postcode, country = None, None, region.strip()\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n            'addr_full': street_address.replace(u'\\u00a0', ' ').strip(', '),\n            'city': response.xpath('//span[@itemprop=\"addressLocality\"]/text()').extract_first().replace(u'\\u00a0', ' ').strip(', '),\n            'state': state,\n            'postcode': postcode,\n            'country': country,\n            'phone': response.xpath('//div[@class=\"booking\"]/a/text()').extract_first(),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse(self, response):\n\n        hotel_parsers = {\n            'holidayinn': self.parse_hotel,\n            'crowneplaza': self.parse_crowne_plaza,\n            'holidayinnexpress': self.parse_hotel,\n            'hotelindigo': self.parse_hotel,\n            'candlewood': self.parse_candlewood_staybridge,\n            'staybridge': self.parse_candlewood_staybridge,\n            'holidayinnresorts': self.parse_hotel,\n            'intercontinental': self.parse_hotel,\n            'regent': self.parse_regent,\n            'hotels': self.parse_hotel,  # vocos\n            'kimptonhotels': self.parse_kimpton,\n            'holidayinnclubvacations': self.parse_hotel,\n            'evenhotels': self.parse_hotel,\n            'avidhotels': self.parse_hotel,\n            'hualuxe': self.parse_hotel\n        }\n\n        hotel_urls = response.xpath('//div[@class=\"hotelList\"]//div[contains(@class, \"hotelItem\")]//a[contains(@class, \"hotel-name\")]/@href').extract()\n        if hotel_urls:\n            for url in hotel_urls:\n                hotel_type = re.search(r'ihg.com/(.*?)/', response.urljoin(url), re.IGNORECASE).group(1)\n\n                yield scrapy.Request(response.urljoin(url),\n                                     callback=hotel_parsers[hotel_type])\n\n        else:\n            urls = response.xpath('//li[@class=\"listingItem\"]/a/@href').extract()\n            for url in urls:\n                yield scrapy.Request(response.urljoin(url))\n", "path": "locations/spiders/ihg_hotels.py"}], "after_files": [{"content": "import json\nimport re\nimport scrapy\n\nfrom locations.items import GeojsonPointItem\n\n\nclass IHGHotels(scrapy.Spider):\n\n    name = \"ihg_hotels\"\n    # allowed_domains = [\"ihg.com\"]  # the Kimpton hotels each have their own domains\n    download_delay = 0.5\n\n    start_urls = (\n        'https://www.ihg.com/holidayinn/destinations/us/en/explore',\n        'https://www.ihg.com/armyhotels/hotels/us/en/installations',\n    )\n\n    def parse_hotel(self, response):\n        if 'hoteldetail' not in response.url:\n            # got redirected back to search page\n            return\n\n        street_address = \" \".join(response.xpath('//span[@itemprop=\"streetAddress\"]/p/text()').extract())\n        if not street_address:\n            street_address = response.xpath('//span[@itemprop=\"streetAddress\"]/text()').extract_first()\n\n        city = response.xpath('//span[@itemprop=\"addressLocality\"]/text()').extract_first()\n        state = response.xpath('//span[@itemprop=\"addressRegion\"]/text()').extract_first()\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n            'addr_full': street_address.replace(u'\\u00a0', ' ').strip(', ') if street_address else None,\n            'city': city.replace(u'\\u00a0', ' ').strip(', ') if city else None,\n            'state': state.replace(u'\\u00a0', ' ').strip(', ') if state else None,\n            'postcode': response.xpath('//span[@itemprop=\"postalCode\"]/text()').extract_first(),\n            'country': response.xpath('//span[@itemprop=\"addressCountry\"]/text()').extract_first(),\n            'phone': (response.xpath('//span[@itemprop=\"telephone\"]/text()').extract_first() or '').strip('| '),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_kimpton(self, response):\n        url = response.xpath('//a[contains(text(), \"VISIT HOTEL WEBSITE\")]/@href').extract_first()\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n        }\n        if not url:  # \"opening soon\" hotels just have teaser pages\n            return\n        url = url.split('?')[0]  # remove querystring\n        yield scrapy.Request(url, callback=self.parse_kimpton_data, meta={\"properties\": properties})\n\n    def parse_kimpton_data(self, response):\n        properties = response.meta[\"properties\"]\n        script = response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first()\n        if script:\n            data = json.loads(script)\n        else:\n            data = {}\n        if 'name' in data:\n            properties.update({\n                'name': data[\"name\"],\n                'addr_full': data[\"address\"][\"streetAddress\"],\n                'city': data[\"address\"][\"addressLocality\"],\n                'state': data[\"address\"].get(\"addressRegion\"),\n                'postcode': data[\"address\"][\"postalCode\"],\n                'country': data[\"address\"].get(\"addressCountry\"),\n                'phone': data.get(\"telephone\"),\n                'website': data[\"url\"]\n            })\n\n        else:\n            street_address = \" \".join(response.xpath('//span[@itemprop=\"streetAddress\"]/p/text()').extract())\n            if not street_address:\n                street_address = response.xpath('//span[@itemprop=\"streetAddress\"]/text()').extract_first()\n\n            city = response.xpath('//span[@itemprop=\"addressLocality\"]/text()').extract_first()\n            state = response.xpath('//span[@itemprop=\"addressRegion\"]/text()').extract_first()\n\n            properties.update({\n                'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n                'addr_full': street_address.replace(u'\\u00a0', ' ').strip(', ') if street_address else None,\n                'city': city.replace(u'\\u00a0', ' ').strip(', ') if city else None,\n                'state': state.replace(u'\\u00a0', ' ') if state else None,\n                'postcode': response.xpath('//span[@itemprop=\"postalCode\"]/text()').extract_first(),\n                'country': response.xpath('//span[@itemprop=\"addressCountry\"]/text()').extract_first(),\n                'phone': (response.xpath('//span[@itemprop=\"telephone\"]/text()').extract_first() or '').strip('| '),\n                'website': response.url,\n            })\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_regent(self, response):\n        data = json.loads(response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first())\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': data[\"name\"],\n            'addr_full': data[\"address\"][\"streetAddress\"],\n            'city': data[\"address\"][\"addressLocality\"],\n            'state': data[\"address\"].get(\"addressRegion\"),\n            'postcode': data[\"address\"][\"postalCode\"],\n            'country': data[\"address\"][\"addressCountry\"],\n            'phone': data[\"telephone\"],\n            'lat': float(data[\"geo\"][\"latitude\"]),\n            'lon': float(data[\"geo\"][\"longitude\"]),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_crowne_plaza(self, response):\n        address = response.xpath('//a[@class=\"hotel-home\"]/text()').extract_first().strip()\n\n        address_parts = address.split('|')\n\n        if len(address_parts) == 4:  # international addresses\n            addr_city, postcode, country, _ = address_parts\n            state = ''\n        else:  # us addresses\n            addr_city, state, postcode, country, _ = address_parts\n\n        street_address = \",\".join(addr_city.split(',')[0:-1])\n        city = addr_city.split(',')[-1]\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n            'addr_full': street_address.strip(),\n            'city': city.strip(),\n            'state': state.strip(),\n            'postcode': postcode.strip(),\n            'country': country.strip(),\n            'phone': response.xpath('//div[@class=\"new-hinfo-address\"]/p/a[2]/text()').extract_first(),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_candlewood_staybridge(self, response):\n        if 'hoteldetail' not in response.url:\n            # got redirected back to search page\n            return\n\n        street_address = \" \".join(response.xpath('//span[@itemprop=\"streetAddress\"]/p/text()').extract())\n        if not street_address:\n            street_address = response.xpath('//span[@itemprop=\"streetAddress\"]/text()').extract_first()\n\n        region = response.xpath('//span[@itemprop=\"addressRegion\"]/text()').extract_first().replace(u'\\u00a0',' ')\n\n        match = re.search(r'([a-z]+)\\s(\\d+)\\s(.*)', region, re.IGNORECASE)\n        if match:\n            state, postcode, country = match.groups()\n        else:\n            state, postcode, country = None, None, region.strip()\n\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n            'addr_full': street_address.replace(u'\\u00a0', ' ').strip(', '),\n            'city': response.xpath('//span[@itemprop=\"addressLocality\"]/text()').extract_first().replace(u'\\u00a0', ' ').strip(', '),\n            'state': state,\n            'postcode': postcode,\n            'country': country,\n            'phone': response.xpath('//div[@class=\"booking\"]/a/text()').extract_first(),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse_army_hotel(self, response):\n        properties = {\n            'ref': \"_\".join(re.search(r'en/(.*)/(.*)/hoteldetail', response.url).groups()),\n            'name': response.xpath('//meta[@property=\"og:title\"]/@content').extract_first(),\n            'addr_full': response.xpath('//meta[@property=\"business:contact_data:street_address\"]/@content').extract_first(),\n            'city': response.xpath('//meta[@property=\"business:contact_data:locality\"]/@content').extract_first(),\n            'state': response.xpath('//meta[@property=\"business:contact_data:region\"]/@content').extract_first(),\n            'postcode': response.xpath('//meta[@property=\"business:contact_data:postal_code\"]/@content').extract_first(),\n            'country': response.xpath('//meta[@property=\"business:contact_data:country_name\"]/@content').extract_first(),\n            'phone': (response.xpath('//span[@title=\"Hotel Front Desk:\"]/span/text()').extract_first() or \"\").strip(),\n            'lat': float(response.xpath('//meta[@property=\"place:location:latitude\"]/@content').extract_first()),\n            'lon': float(response.xpath('//meta[@property=\"place:location:longitude\"]/@content').extract_first()),\n            'website': response.url,\n        }\n\n        yield GeojsonPointItem(**properties)\n\n    def parse(self, response):\n\n        hotel_parsers = {\n            'holidayinn': self.parse_hotel,\n            'crowneplaza': self.parse_crowne_plaza,\n            'holidayinnexpress': self.parse_hotel,\n            'hotelindigo': self.parse_hotel,\n            'candlewood': self.parse_candlewood_staybridge,\n            'staybridge': self.parse_candlewood_staybridge,\n            'holidayinnresorts': self.parse_hotel,\n            'intercontinental': self.parse_hotel,\n            'regent': self.parse_regent,\n            'hotels': self.parse_hotel,  # vocos\n            'kimptonhotels': self.parse_kimpton,\n            'holidayinnclubvacations': self.parse_hotel,\n            'evenhotels': self.parse_hotel,\n            'avidhotels': self.parse_hotel,\n            'hualuxe': self.parse_hotel,\n            'armyhotels': self.parse_army_hotel\n        }\n\n        hotel_urls = response.xpath('//div[@class=\"hotelList\"]//div[contains(@class, \"hotelItem\")]//a[contains(@class, \"hotel-name\")]/@href').extract()\n\n        if 'armyhotels' in response.url:\n            hotel_urls = response.xpath('//div[@id=\"hotelListWrap\"]//a/@href').extract()\n\n        if hotel_urls:\n            for url in hotel_urls:\n                hotel_type = re.search(r'ihg.com/(.*?)/', response.urljoin(url), re.IGNORECASE).group(1)\n\n                yield scrapy.Request(response.urljoin(url),\n                                     callback=hotel_parsers[hotel_type])\n\n        else:\n            urls = response.xpath('//li[@class=\"listingItem\"]/a/@href').extract()\n            for url in urls:\n                yield scrapy.Request(response.urljoin(url))\n", "path": "locations/spiders/ihg_hotels.py"}]}