We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
MN: People scraper return none
State: _MN__  (be sure to include in ticket title)

when attempting to scrape MN people the following error is returned:

`
pupa.exceptions.ScrapeError: no objects returned from MNPersonScraper scrape
`

any advice ?

The CSV needed is still available. MN did recently update their site, and I was getting an assertion error that was fixed with I updated the links it was looking for. But now getting the "no objects returned" error :/

[dpaste](http://dpaste.com/1EKJ757)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `openstates/mn/people.py`
Content:
```
1 import collections
2 import logging
3 import lxml.html
4 import re
5 
6 from pupa.scrape import Person, Scraper
7 from spatula import Page, CSV, Spatula
8 from openstates.utils import validate_phone_number, validate_email_address
9 
10 PARTIES = {
11     'DFL': 'Democratic-Farmer-Labor',
12     'R': 'Republican',
13 }
14 
15 
16 class SenList(CSV):
17     url = 'http://www.senate.mn/members/member_list_ascii.php?ls='
18     _html_url = 'http://www.senate.mn/members/index.php'
19 
20     def __init__(self, scraper, url=None, *, obj=None, **kwargs):
21         super().__init__(scraper, url=url, obj=obj, **kwargs)
22         self._scrape_extra_info()
23 
24     def _scrape_extra_info(self):
25         self.extra_info = collections.defaultdict(dict)
26 
27         resp = self.scraper.get(self._html_url)
28         doc = lxml.html.fromstring(resp.text)
29         doc.make_links_absolute(self._html_url)
30         xpath = ('//div[@id="hide_show_alpha_all"]'
31                  '//td[@style="vertical-align:top;"]')
32         for td in doc.xpath(xpath):
33             main_link, email_link = td.xpath('.//a')
34             name = main_link.text_content().split(' (')[0]
35             leg = self.extra_info[name]
36             leg['office_phone'] = next(filter(
37                 lambda string: re.match(r'\d{3}-\d{3}-\d{4}', string),
38                 td.xpath('.//p/text()')
39             )).strip()
40             leg['url'] = main_link.get('href')
41             leg['image'] = td.xpath('./preceding-sibling::td//img/@src')[0]
42             if 'mailto:' in email_link.get('href'):
43                 leg['email'] = email_link.get('href').replace('mailto:', '')
44 
45         logger = logging.getLogger("pupa")
46         logger.info('collected preliminary data on {} legislators'
47                     .format(len(self.extra_info)))
48         assert self.extra_info
49 
50     def handle_list_item(self, row):
51         if not row['First Name']:
52             return
53         name = '{} {}'.format(row['First Name'], row['Last Name'])
54         party = PARTIES[row['Party']]
55         leg = Person(name=name, district=row['District'].lstrip('0'),
56                      party=party, primary_org='upper', role='Senator',
57                      image=self.extra_info[name]['image'])
58         leg.add_link(self.extra_info[name]['url'])
59         leg.add_contact_detail(type='voice',
60                                value=self.extra_info[name]['office_phone'], note='capitol')
61         if 'email' in self.extra_info[name]:
62             leg.add_contact_detail(type='email',
63                                    value=self.extra_info[name]['email'], note='capitol')
64 
65         row['Zipcode'] = row['Zipcode'].strip()
66         # Accommodate for multiple address column naming conventions.
67         address1_fields = [row.get('Address'), row.get('Office Building')]
68         address2_fields = [row.get('Address2'), row.get('Office Address')]
69         row['Address'] = next((a for a in address1_fields if a is not
70                                None), False)
71         row['Address2'] = next((a for a in address2_fields if a is not
72                                 None), False)
73 
74         if (a in row['Address2'] for a in ['95 University Avenue W',
75                                            '100 Rev. Dr. Martin Luther King']):
76             address = ('{Address}\n{Address2}\n{City}, {State} {Zipcode}'
77                        .format(**row))
78             if 'Rm. Number' in row:
79                 address = '{0} {1}'.format(row['Rm. Number'], address)
80             leg.add_contact_detail(type='address', value=address,
81                                    note='capitol')
82         elif row['Address2']:
83             address = ('{Address}\n{Address2}\n{City}, {State} {Zipcode}'
84                        .format(**row))
85             leg.add_contact_detail(type='address', value=address,
86                                    note='district')
87         else:
88             address = '{Address}\n{City}, {State} {Zipcode}'.format(**row)
89             leg.add_contact_detail(type='address', value=address,
90                                    note='district')
91 
92         leg.add_source(self.url)
93         leg.add_source(self._html_url)
94 
95         return leg
96 
97     def handle_page(self):
98         yield super(SenList, self).handle_page()
99 
100 
101 class RepList(Page):
102     url = 'http://www.house.leg.state.mn.us/members/hmem.asp'
103     list_xpath = '//div[@id="hide_show_alpha_all"]/table/tr/td/table/tr'
104 
105     def handle_list_item(self, item):
106         photo_url = item.xpath('./td[1]/a/img/@src')[0]
107         info_nodes = item.xpath('./td[2]/p/a')
108         name_text = info_nodes[0].xpath('./b/text()')[0]
109         url = info_nodes[0].get('href')
110 
111         name_match = re.match(r'^(.+)\(([0-9]{2}[AB]), ([A-Z]+)\)$', name_text)
112         name = name_match.group(1).strip()
113         district = name_match.group(2).lstrip('0').upper()
114         party_text = name_match.group(3)
115         party = PARTIES[party_text]
116 
117         info_texts = [x.strip() for x in item.xpath(
118             './td[2]/p/text()[normalize-space() and preceding-sibling::br]'
119         ) if x.strip()]
120         address = '\n'.join((info_texts[0], info_texts[1]))
121 
122         phone_text = info_texts[2]
123         if validate_phone_number(phone_text):
124             phone = phone_text
125 
126         email_node = info_nodes[1]
127         email_text = email_node.text
128         email_text = email_text.replace('Email: ', '').strip()
129         if validate_email_address(email_text):
130             email = email_text
131 
132         rep = Person(name=name, district=district, party=party,
133                      primary_org='lower', role='Representative',
134                      image=photo_url)
135         rep.add_link(url)
136         rep.add_contact_detail(type='address', value=address, note='capitol')
137         rep.add_contact_detail(type='voice', value=phone, note='capitol')
138         rep.add_contact_detail(type='email', value=email, note='capitol')
139         rep.add_source(self.url)
140 
141         yield rep
142 
143 
144 class MNPersonScraper(Scraper, Spatula):
145     def scrape(self):
146         yield from self.scrape_page_items(SenList)
147         yield from self.scrape_page_items(RepList)
148
```
--- 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/openstates/mn/people.py b/openstates/mn/people.py
--- a/openstates/mn/people.py
+++ b/openstates/mn/people.py
@@ -27,18 +27,18 @@
         resp = self.scraper.get(self._html_url)
         doc = lxml.html.fromstring(resp.text)
         doc.make_links_absolute(self._html_url)
-        xpath = ('//div[@id="hide_show_alpha_all"]'
-                 '//td[@style="vertical-align:top;"]')
-        for td in doc.xpath(xpath):
-            main_link, email_link = td.xpath('.//a')
+        xpath = ('//div[@id="alphabetically"]'
+                 '//div[@class="media my-3"]')
+        for div in doc.xpath(xpath):
+            main_link, email_link = filter(lambda link: link.get('href'), div.xpath('.//a'))
             name = main_link.text_content().split(' (')[0]
             leg = self.extra_info[name]
             leg['office_phone'] = next(filter(
-                lambda string: re.match(r'\d{3}-\d{3}-\d{4}', string),
-                td.xpath('.//p/text()')
+                lambda string: re.match(r'\d{3}-\d{3}-\d{4}', string.strip()),
+                div.xpath('.//text()')
             )).strip()
             leg['url'] = main_link.get('href')
-            leg['image'] = td.xpath('./preceding-sibling::td//img/@src')[0]
+            leg['image'] = div.xpath('.//img/@src')[0]
             if 'mailto:' in email_link.get('href'):
                 leg['email'] = email_link.get('href').replace('mailto:', '')
 
@@ -100,13 +100,12 @@
 
 class RepList(Page):
     url = 'http://www.house.leg.state.mn.us/members/hmem.asp'
-    list_xpath = '//div[@id="hide_show_alpha_all"]/table/tr/td/table/tr'
+    list_xpath = '//div[@id="Alpha"]//div[@class="media my-3"]'
 
     def handle_list_item(self, item):
-        photo_url = item.xpath('./td[1]/a/img/@src')[0]
-        info_nodes = item.xpath('./td[2]/p/a')
-        name_text = info_nodes[0].xpath('./b/text()')[0]
-        url = info_nodes[0].get('href')
+        photo_url = item.xpath('./img/@src')[0]
+        url = item.xpath('.//h5/a/@href')[0]
+        name_text = item.xpath('.//h5/a/b/text()')[0]
 
         name_match = re.match(r'^(.+)\(([0-9]{2}[AB]), ([A-Z]+)\)$', name_text)
         name = name_match.group(1).strip()
@@ -115,7 +114,7 @@
         party = PARTIES[party_text]
 
         info_texts = [x.strip() for x in item.xpath(
-            './td[2]/p/text()[normalize-space() and preceding-sibling::br]'
+            './div/text()[normalize-space()]'
         ) if x.strip()]
         address = '\n'.join((info_texts[0], info_texts[1]))
 
@@ -123,9 +122,7 @@
         if validate_phone_number(phone_text):
             phone = phone_text
 
-        email_node = info_nodes[1]
-        email_text = email_node.text
-        email_text = email_text.replace('Email: ', '').strip()
+        email_text = item.xpath('.//a/@href')[1].replace('mailto:', '').strip()
         if validate_email_address(email_text):
             email = email_text

{"golden_diff": "diff --git a/openstates/mn/people.py b/openstates/mn/people.py\n--- a/openstates/mn/people.py\n+++ b/openstates/mn/people.py\n@@ -27,18 +27,18 @@\n         resp = self.scraper.get(self._html_url)\n         doc = lxml.html.fromstring(resp.text)\n         doc.make_links_absolute(self._html_url)\n-        xpath = ('//div[@id=\"hide_show_alpha_all\"]'\n-                 '//td[@style=\"vertical-align:top;\"]')\n-        for td in doc.xpath(xpath):\n-            main_link, email_link = td.xpath('.//a')\n+        xpath = ('//div[@id=\"alphabetically\"]'\n+                 '//div[@class=\"media my-3\"]')\n+        for div in doc.xpath(xpath):\n+            main_link, email_link = filter(lambda link: link.get('href'), div.xpath('.//a'))\n             name = main_link.text_content().split(' (')[0]\n             leg = self.extra_info[name]\n             leg['office_phone'] = next(filter(\n-                lambda string: re.match(r'\\d{3}-\\d{3}-\\d{4}', string),\n-                td.xpath('.//p/text()')\n+                lambda string: re.match(r'\\d{3}-\\d{3}-\\d{4}', string.strip()),\n+                div.xpath('.//text()')\n             )).strip()\n             leg['url'] = main_link.get('href')\n-            leg['image'] = td.xpath('./preceding-sibling::td//img/@src')[0]\n+            leg['image'] = div.xpath('.//img/@src')[0]\n             if 'mailto:' in email_link.get('href'):\n                 leg['email'] = email_link.get('href').replace('mailto:', '')\n \n@@ -100,13 +100,12 @@\n \n class RepList(Page):\n     url = 'http://www.house.leg.state.mn.us/members/hmem.asp'\n-    list_xpath = '//div[@id=\"hide_show_alpha_all\"]/table/tr/td/table/tr'\n+    list_xpath = '//div[@id=\"Alpha\"]//div[@class=\"media my-3\"]'\n \n     def handle_list_item(self, item):\n-        photo_url = item.xpath('./td[1]/a/img/@src')[0]\n-        info_nodes = item.xpath('./td[2]/p/a')\n-        name_text = info_nodes[0].xpath('./b/text()')[0]\n-        url = info_nodes[0].get('href')\n+        photo_url = item.xpath('./img/@src')[0]\n+        url = item.xpath('.//h5/a/@href')[0]\n+        name_text = item.xpath('.//h5/a/b/text()')[0]\n \n         name_match = re.match(r'^(.+)\\(([0-9]{2}[AB]), ([A-Z]+)\\)$', name_text)\n         name = name_match.group(1).strip()\n@@ -115,7 +114,7 @@\n         party = PARTIES[party_text]\n \n         info_texts = [x.strip() for x in item.xpath(\n-            './td[2]/p/text()[normalize-space() and preceding-sibling::br]'\n+            './div/text()[normalize-space()]'\n         ) if x.strip()]\n         address = '\\n'.join((info_texts[0], info_texts[1]))\n \n@@ -123,9 +122,7 @@\n         if validate_phone_number(phone_text):\n             phone = phone_text\n \n-        email_node = info_nodes[1]\n-        email_text = email_node.text\n-        email_text = email_text.replace('Email: ', '').strip()\n+        email_text = item.xpath('.//a/@href')[1].replace('mailto:', '').strip()\n         if validate_email_address(email_text):\n             email = email_text\n", "issue": "MN: People scraper return none\nState: _MN__  (be sure to include in ticket title)\r\n\r\nwhen attempting to scrape MN people the following error is returned:\r\n\r\n`\r\npupa.exceptions.ScrapeError: no objects returned from MNPersonScraper scrape\r\n`\r\n\r\nany advice ?\r\n\r\nThe CSV needed is still available. MN did recently update their site, and I was getting an assertion error that was fixed with I updated the links it was looking for. But now getting the \"no objects returned\" error :/\r\n\r\n[dpaste](http://dpaste.com/1EKJ757)\r\n\n", "before_files": [{"content": "import collections\nimport logging\nimport lxml.html\nimport re\n\nfrom pupa.scrape import Person, Scraper\nfrom spatula import Page, CSV, Spatula\nfrom openstates.utils import validate_phone_number, validate_email_address\n\nPARTIES = {\n    'DFL': 'Democratic-Farmer-Labor',\n    'R': 'Republican',\n}\n\n\nclass SenList(CSV):\n    url = 'http://www.senate.mn/members/member_list_ascii.php?ls='\n    _html_url = 'http://www.senate.mn/members/index.php'\n\n    def __init__(self, scraper, url=None, *, obj=None, **kwargs):\n        super().__init__(scraper, url=url, obj=obj, **kwargs)\n        self._scrape_extra_info()\n\n    def _scrape_extra_info(self):\n        self.extra_info = collections.defaultdict(dict)\n\n        resp = self.scraper.get(self._html_url)\n        doc = lxml.html.fromstring(resp.text)\n        doc.make_links_absolute(self._html_url)\n        xpath = ('//div[@id=\"hide_show_alpha_all\"]'\n                 '//td[@style=\"vertical-align:top;\"]')\n        for td in doc.xpath(xpath):\n            main_link, email_link = td.xpath('.//a')\n            name = main_link.text_content().split(' (')[0]\n            leg = self.extra_info[name]\n            leg['office_phone'] = next(filter(\n                lambda string: re.match(r'\\d{3}-\\d{3}-\\d{4}', string),\n                td.xpath('.//p/text()')\n            )).strip()\n            leg['url'] = main_link.get('href')\n            leg['image'] = td.xpath('./preceding-sibling::td//img/@src')[0]\n            if 'mailto:' in email_link.get('href'):\n                leg['email'] = email_link.get('href').replace('mailto:', '')\n\n        logger = logging.getLogger(\"pupa\")\n        logger.info('collected preliminary data on {} legislators'\n                    .format(len(self.extra_info)))\n        assert self.extra_info\n\n    def handle_list_item(self, row):\n        if not row['First Name']:\n            return\n        name = '{} {}'.format(row['First Name'], row['Last Name'])\n        party = PARTIES[row['Party']]\n        leg = Person(name=name, district=row['District'].lstrip('0'),\n                     party=party, primary_org='upper', role='Senator',\n                     image=self.extra_info[name]['image'])\n        leg.add_link(self.extra_info[name]['url'])\n        leg.add_contact_detail(type='voice',\n                               value=self.extra_info[name]['office_phone'], note='capitol')\n        if 'email' in self.extra_info[name]:\n            leg.add_contact_detail(type='email',\n                                   value=self.extra_info[name]['email'], note='capitol')\n\n        row['Zipcode'] = row['Zipcode'].strip()\n        # Accommodate for multiple address column naming conventions.\n        address1_fields = [row.get('Address'), row.get('Office Building')]\n        address2_fields = [row.get('Address2'), row.get('Office Address')]\n        row['Address'] = next((a for a in address1_fields if a is not\n                               None), False)\n        row['Address2'] = next((a for a in address2_fields if a is not\n                                None), False)\n\n        if (a in row['Address2'] for a in ['95 University Avenue W',\n                                           '100 Rev. Dr. Martin Luther King']):\n            address = ('{Address}\\n{Address2}\\n{City}, {State} {Zipcode}'\n                       .format(**row))\n            if 'Rm. Number' in row:\n                address = '{0} {1}'.format(row['Rm. Number'], address)\n            leg.add_contact_detail(type='address', value=address,\n                                   note='capitol')\n        elif row['Address2']:\n            address = ('{Address}\\n{Address2}\\n{City}, {State} {Zipcode}'\n                       .format(**row))\n            leg.add_contact_detail(type='address', value=address,\n                                   note='district')\n        else:\n            address = '{Address}\\n{City}, {State} {Zipcode}'.format(**row)\n            leg.add_contact_detail(type='address', value=address,\n                                   note='district')\n\n        leg.add_source(self.url)\n        leg.add_source(self._html_url)\n\n        return leg\n\n    def handle_page(self):\n        yield super(SenList, self).handle_page()\n\n\nclass RepList(Page):\n    url = 'http://www.house.leg.state.mn.us/members/hmem.asp'\n    list_xpath = '//div[@id=\"hide_show_alpha_all\"]/table/tr/td/table/tr'\n\n    def handle_list_item(self, item):\n        photo_url = item.xpath('./td[1]/a/img/@src')[0]\n        info_nodes = item.xpath('./td[2]/p/a')\n        name_text = info_nodes[0].xpath('./b/text()')[0]\n        url = info_nodes[0].get('href')\n\n        name_match = re.match(r'^(.+)\\(([0-9]{2}[AB]), ([A-Z]+)\\)$', name_text)\n        name = name_match.group(1).strip()\n        district = name_match.group(2).lstrip('0').upper()\n        party_text = name_match.group(3)\n        party = PARTIES[party_text]\n\n        info_texts = [x.strip() for x in item.xpath(\n            './td[2]/p/text()[normalize-space() and preceding-sibling::br]'\n        ) if x.strip()]\n        address = '\\n'.join((info_texts[0], info_texts[1]))\n\n        phone_text = info_texts[2]\n        if validate_phone_number(phone_text):\n            phone = phone_text\n\n        email_node = info_nodes[1]\n        email_text = email_node.text\n        email_text = email_text.replace('Email: ', '').strip()\n        if validate_email_address(email_text):\n            email = email_text\n\n        rep = Person(name=name, district=district, party=party,\n                     primary_org='lower', role='Representative',\n                     image=photo_url)\n        rep.add_link(url)\n        rep.add_contact_detail(type='address', value=address, note='capitol')\n        rep.add_contact_detail(type='voice', value=phone, note='capitol')\n        rep.add_contact_detail(type='email', value=email, note='capitol')\n        rep.add_source(self.url)\n\n        yield rep\n\n\nclass MNPersonScraper(Scraper, Spatula):\n    def scrape(self):\n        yield from self.scrape_page_items(SenList)\n        yield from self.scrape_page_items(RepList)\n", "path": "openstates/mn/people.py"}], "after_files": [{"content": "import collections\nimport logging\nimport lxml.html\nimport re\n\nfrom pupa.scrape import Person, Scraper\nfrom spatula import Page, CSV, Spatula\nfrom openstates.utils import validate_phone_number, validate_email_address\n\nPARTIES = {\n    'DFL': 'Democratic-Farmer-Labor',\n    'R': 'Republican',\n}\n\n\nclass SenList(CSV):\n    url = 'http://www.senate.mn/members/member_list_ascii.php?ls='\n    _html_url = 'http://www.senate.mn/members/index.php'\n\n    def __init__(self, scraper, url=None, *, obj=None, **kwargs):\n        super().__init__(scraper, url=url, obj=obj, **kwargs)\n        self._scrape_extra_info()\n\n    def _scrape_extra_info(self):\n        self.extra_info = collections.defaultdict(dict)\n\n        resp = self.scraper.get(self._html_url)\n        doc = lxml.html.fromstring(resp.text)\n        doc.make_links_absolute(self._html_url)\n        xpath = ('//div[@id=\"alphabetically\"]'\n                 '//div[@class=\"media my-3\"]')\n        for div in doc.xpath(xpath):\n            main_link, email_link = filter(lambda link: link.get('href'), div.xpath('.//a'))\n            name = main_link.text_content().split(' (')[0]\n            leg = self.extra_info[name]\n            leg['office_phone'] = next(filter(\n                lambda string: re.match(r'\\d{3}-\\d{3}-\\d{4}', string.strip()),\n                div.xpath('.//text()')\n            )).strip()\n            leg['url'] = main_link.get('href')\n            leg['image'] = div.xpath('.//img/@src')[0]\n            if 'mailto:' in email_link.get('href'):\n                leg['email'] = email_link.get('href').replace('mailto:', '')\n\n        logger = logging.getLogger(\"pupa\")\n        logger.info('collected preliminary data on {} legislators'\n                    .format(len(self.extra_info)))\n        assert self.extra_info\n\n    def handle_list_item(self, row):\n        if not row['First Name']:\n            return\n        name = '{} {}'.format(row['First Name'], row['Last Name'])\n        party = PARTIES[row['Party']]\n        leg = Person(name=name, district=row['District'].lstrip('0'),\n                     party=party, primary_org='upper', role='Senator',\n                     image=self.extra_info[name]['image'])\n        leg.add_link(self.extra_info[name]['url'])\n        leg.add_contact_detail(type='voice',\n                               value=self.extra_info[name]['office_phone'], note='capitol')\n        if 'email' in self.extra_info[name]:\n            leg.add_contact_detail(type='email',\n                                   value=self.extra_info[name]['email'], note='capitol')\n\n        row['Zipcode'] = row['Zipcode'].strip()\n        # Accommodate for multiple address column naming conventions.\n        address1_fields = [row.get('Address'), row.get('Office Building')]\n        address2_fields = [row.get('Address2'), row.get('Office Address')]\n        row['Address'] = next((a for a in address1_fields if a is not\n                               None), False)\n        row['Address2'] = next((a for a in address2_fields if a is not\n                                None), False)\n\n        if (a in row['Address2'] for a in ['95 University Avenue W',\n                                           '100 Rev. Dr. Martin Luther King']):\n            address = ('{Address}\\n{Address2}\\n{City}, {State} {Zipcode}'\n                       .format(**row))\n            if 'Rm. Number' in row:\n                address = '{0} {1}'.format(row['Rm. Number'], address)\n            leg.add_contact_detail(type='address', value=address,\n                                   note='capitol')\n        elif row['Address2']:\n            address = ('{Address}\\n{Address2}\\n{City}, {State} {Zipcode}'\n                       .format(**row))\n            leg.add_contact_detail(type='address', value=address,\n                                   note='district')\n        else:\n            address = '{Address}\\n{City}, {State} {Zipcode}'.format(**row)\n            leg.add_contact_detail(type='address', value=address,\n                                   note='district')\n\n        leg.add_source(self.url)\n        leg.add_source(self._html_url)\n\n        return leg\n\n    def handle_page(self):\n        yield super(SenList, self).handle_page()\n\n\nclass RepList(Page):\n    url = 'http://www.house.leg.state.mn.us/members/hmem.asp'\n    list_xpath = '//div[@id=\"Alpha\"]//div[@class=\"media my-3\"]'\n\n    def handle_list_item(self, item):\n        photo_url = item.xpath('./img/@src')[0]\n        url = item.xpath('.//h5/a/@href')[0]\n        name_text = item.xpath('.//h5/a/b/text()')[0]\n\n        name_match = re.match(r'^(.+)\\(([0-9]{2}[AB]), ([A-Z]+)\\)$', name_text)\n        name = name_match.group(1).strip()\n        district = name_match.group(2).lstrip('0').upper()\n        party_text = name_match.group(3)\n        party = PARTIES[party_text]\n\n        info_texts = [x.strip() for x in item.xpath(\n            './div/text()[normalize-space()]'\n        ) if x.strip()]\n        address = '\\n'.join((info_texts[0], info_texts[1]))\n\n        phone_text = info_texts[2]\n        if validate_phone_number(phone_text):\n            phone = phone_text\n\n        email_text = item.xpath('.//a/@href')[1].replace('mailto:', '').strip()\n        if validate_email_address(email_text):\n            email = email_text\n\n        rep = Person(name=name, district=district, party=party,\n                     primary_org='lower', role='Representative',\n                     image=photo_url)\n        rep.add_link(url)\n        rep.add_contact_detail(type='address', value=address, note='capitol')\n        rep.add_contact_detail(type='voice', value=phone, note='capitol')\n        rep.add_contact_detail(type='email', value=email, note='capitol')\n        rep.add_source(self.url)\n\n        yield rep\n\n\nclass MNPersonScraper(Scraper, Spatula):\n    def scrape(self):\n        yield from self.scrape_page_items(SenList)\n        yield from self.scrape_page_items(RepList)\n", "path": "openstates/mn/people.py"}]}