warn-scraper 1.2.155.dev0__py3-none-any.whl → 1.2.157.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- warn/scrapers/al.py +34 -39
- warn/utils.py +20 -3
- {warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/METADATA +1 -1
- {warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/RECORD +8 -8
- {warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/WHEEL +0 -0
- {warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/entry_points.txt +0 -0
- {warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/licenses/LICENSE +0 -0
- {warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/top_level.txt +0 -0
warn/scrapers/al.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import csv
|
|
1
2
|
import logging
|
|
2
|
-
import
|
|
3
|
+
from io import StringIO
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
|
-
from bs4 import BeautifulSoup
|
|
6
|
-
|
|
7
6
|
from .. import utils
|
|
7
|
+
from ..cache import Cache
|
|
8
8
|
|
|
9
|
-
__authors__ = ["zstumgoren", "Dilcia19"]
|
|
9
|
+
__authors__ = ["zstumgoren", "Dilcia19", "stucka"]
|
|
10
10
|
__tags__ = [
|
|
11
|
-
"
|
|
11
|
+
"csv",
|
|
12
12
|
]
|
|
13
13
|
__source__ = {
|
|
14
14
|
"name": "Alabama Department of Commerce",
|
|
@@ -31,45 +31,40 @@ def scrape(
|
|
|
31
31
|
|
|
32
32
|
Returns: the Path where the file is written
|
|
33
33
|
"""
|
|
34
|
+
cache = Cache()
|
|
34
35
|
output_csv = data_dir / "al.csv"
|
|
36
|
+
|
|
35
37
|
# page = utils.get_url("https://www.madeinalabama.com/warn-list/")
|
|
36
38
|
# URL change in June 2026, maybe led to a HTTP 415 error
|
|
37
|
-
page = utils.get_url("https://workforce.alabama.gov/warn-list/")
|
|
38
|
-
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
raw_header = table_rows.pop(0)
|
|
46
|
-
header_row = _extract_fields_from_row(raw_header, "th")
|
|
47
|
-
output_rows = [header_row]
|
|
48
|
-
# Process remaining rows
|
|
49
|
-
discarded_rows = []
|
|
50
|
-
for table_row in table_rows:
|
|
51
|
-
# Discard bogus data lines (see last lines of source data)
|
|
52
|
-
# based on check of first field ("Closing or Layoff")
|
|
53
|
-
data = _extract_fields_from_row(table_row, "td")
|
|
54
|
-
layoff_type = data[0]
|
|
55
|
-
if re.match(r"(clos|lay)", layoff_type, re.I):
|
|
56
|
-
output_rows.append(data)
|
|
57
|
-
else:
|
|
58
|
-
discarded_rows.append(data)
|
|
59
|
-
if discarded_rows:
|
|
60
|
-
logger.warn(f"Warning: Discarded {len(discarded_rows)} dirty data row(s)")
|
|
61
|
-
utils.write_rows_to_csv(output_csv, output_rows)
|
|
62
|
-
return output_csv
|
|
39
|
+
# page = utils.get_url("https://workforce.alabama.gov/warn-list/")
|
|
40
|
+
# Later in June 2026, they're detecting the automation and blocking us.
|
|
41
|
+
# But say they won't for the CSV download which, OK.
|
|
42
|
+
# No headers on the CSV, which they'll probably realize ... sometime.
|
|
43
|
+
|
|
44
|
+
targeturl = "https://workforce.alabama.gov/documents/warn-list/"
|
|
45
|
+
page = utils.get_url(targeturl).text
|
|
46
|
+
cache.write("al/rawcsv.csv", page)
|
|
63
47
|
|
|
48
|
+
headers = [
|
|
49
|
+
"_id1",
|
|
50
|
+
"action_type",
|
|
51
|
+
"date_notice",
|
|
52
|
+
"date_action",
|
|
53
|
+
"company",
|
|
54
|
+
"location",
|
|
55
|
+
"affected",
|
|
56
|
+
"_id2",
|
|
57
|
+
]
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
fileholder = StringIO(page)
|
|
60
|
+
|
|
61
|
+
reader = list(csv.DictReader(fileholder, fieldnames=headers))
|
|
62
|
+
|
|
63
|
+
utils.write_disparate_dict_rows_to_csv(
|
|
64
|
+
output_csv, reader, mode="w", prefixes=["_id"]
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return output_csv
|
|
73
68
|
|
|
74
69
|
|
|
75
70
|
if __name__ == "__main__":
|
warn/utils.py
CHANGED
|
@@ -59,6 +59,8 @@ def fetch_if_not_cached(filename, url, **kwargs):
|
|
|
59
59
|
url: The URL from which the file may be downloaded.
|
|
60
60
|
Notes: Should this even be in utils vs. cache? Should it exist?
|
|
61
61
|
"""
|
|
62
|
+
if "timeout" not in kwargs:
|
|
63
|
+
kwargs["timeout"] = 120
|
|
62
64
|
create_directory(Path(filename), is_file=True)
|
|
63
65
|
if not os.path.exists(filename):
|
|
64
66
|
logger.debug(f"Fetching {filename} from {url}")
|
|
@@ -81,6 +83,8 @@ def save_if_good_url(filename, url, **kwargs):
|
|
|
81
83
|
Notes: Should this even be in utils vs. cache? Should it exist?
|
|
82
84
|
"""
|
|
83
85
|
create_directory(Path(filename), is_file=True)
|
|
86
|
+
if "timeout" not in kwargs:
|
|
87
|
+
kwargs["timeout"] = 120
|
|
84
88
|
response = requests.get(url, **kwargs)
|
|
85
89
|
if not response.ok:
|
|
86
90
|
logger.error(f"URL {url} fetch failed with {response.status_code}")
|
|
@@ -96,7 +100,7 @@ def save_if_good_url(filename, url, **kwargs):
|
|
|
96
100
|
return success_flag, content
|
|
97
101
|
|
|
98
102
|
|
|
99
|
-
def get_with_zyte(url, json_extras=None):
|
|
103
|
+
def get_with_zyte(url, json_extras=None, **kwargs):
|
|
100
104
|
"""Use Zyte as a proxy server to retrieve data not available without it.
|
|
101
105
|
|
|
102
106
|
Args:
|
|
@@ -119,6 +123,9 @@ def get_with_zyte(url, json_extras=None):
|
|
|
119
123
|
)
|
|
120
124
|
return (None, None)
|
|
121
125
|
|
|
126
|
+
if "timeout" not in kwargs:
|
|
127
|
+
kwargs["timeout"] = 120
|
|
128
|
+
|
|
122
129
|
myjson = {
|
|
123
130
|
"url": url,
|
|
124
131
|
"httpResponseBody": True,
|
|
@@ -129,7 +136,10 @@ def get_with_zyte(url, json_extras=None):
|
|
|
129
136
|
myjson[item] = json_extras[item]
|
|
130
137
|
|
|
131
138
|
api_response = requests.post(
|
|
132
|
-
"https://api.zyte.com/v1/extract",
|
|
139
|
+
"https://api.zyte.com/v1/extract",
|
|
140
|
+
auth=(zyte_api_key, ""),
|
|
141
|
+
json=myjson,
|
|
142
|
+
**kwargs,
|
|
133
143
|
)
|
|
134
144
|
|
|
135
145
|
if not api_response.ok:
|
|
@@ -150,7 +160,7 @@ def get_with_zyte(url, json_extras=None):
|
|
|
150
160
|
return (returnbin, returntext)
|
|
151
161
|
|
|
152
162
|
|
|
153
|
-
def post_with_zyte(url, payload):
|
|
163
|
+
def post_with_zyte(url, payload, **kwargs):
|
|
154
164
|
"""Use Zyte as a proxy server to retrieve data not available without it.
|
|
155
165
|
|
|
156
166
|
Args:
|
|
@@ -175,6 +185,9 @@ def post_with_zyte(url, payload):
|
|
|
175
185
|
)
|
|
176
186
|
return (None, None)
|
|
177
187
|
|
|
188
|
+
if "timeout" not in kwargs:
|
|
189
|
+
kwargs["timeout"] = 120
|
|
190
|
+
|
|
178
191
|
if isinstance(payload, dict):
|
|
179
192
|
payload = json.dumps(payload)
|
|
180
193
|
|
|
@@ -191,6 +204,7 @@ def post_with_zyte(url, payload):
|
|
|
191
204
|
"httpResponseBody": True,
|
|
192
205
|
"followRedirect": True,
|
|
193
206
|
},
|
|
207
|
+
**kwargs,
|
|
194
208
|
)
|
|
195
209
|
|
|
196
210
|
if not api_response.ok:
|
|
@@ -326,6 +340,9 @@ def get_url(
|
|
|
326
340
|
kwargs["headers"] = {}
|
|
327
341
|
kwargs["headers"]["User-Agent"] = user_agent
|
|
328
342
|
|
|
343
|
+
if "timeout" not in kwargs:
|
|
344
|
+
kwargs["timeout"] = 120
|
|
345
|
+
|
|
329
346
|
# Go get it
|
|
330
347
|
if session is not None:
|
|
331
348
|
logger.debug(f"Requesting with session {session}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: warn-scraper
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.157.dev0
|
|
4
4
|
Summary: Command-line interface for downloading WARN Act notices of qualified plant closings and mass layoffs from state government websites
|
|
5
5
|
Author-email: Big Local News <biglocalnews@stanford.edu>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -2,7 +2,7 @@ warn/__init__.py,sha256=A07JFY1TyaPtVIndBa7IvTk13DETqIkLgRdk0A-MCoE,85
|
|
|
2
2
|
warn/cache.py,sha256=QBSHycchvRTkOQfHptOtZeTYiPgLP383jS8MTiGln_c,5969
|
|
3
3
|
warn/cli.py,sha256=ZqyJwICdHFkn2hEgbArj_upbElR9-TSDlYDqyEGeexE,2019
|
|
4
4
|
warn/runner.py,sha256=oeGRybGwpnkQKlPzRMlKxhsDt1GN4PZoX-vUwrsPgos,1894
|
|
5
|
-
warn/utils.py,sha256=
|
|
5
|
+
warn/utils.py,sha256=MTqbzF4h_TlrtBWqH4pEVA-X8yfw6b6ObafSGkqxKb4,13489
|
|
6
6
|
warn/pdfrodent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
warn/pdfrodent/pdfrodent.py,sha256=S2LXkKOdP0Jqzcg10hzGijTu83TMS72DzZqQceslJ70,14914
|
|
8
8
|
warn/platforms/__init__.py,sha256=wIZRDf4tbTuC8oKM4ZrTAtwNgbtMQGzPXMwDYCFyrog,81
|
|
@@ -13,7 +13,7 @@ warn/platforms/job_center/urls.py,sha256=IWhpuzN_xcNdHh23GbZPGvuHCsMcmb03qx3pRn1
|
|
|
13
13
|
warn/platforms/job_center/utils.py,sha256=HdUKgKirmpPP7e4Cu_ZyB3zPVS_p-_ylo-lXFhxK2QM,5696
|
|
14
14
|
warn/scrapers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
warn/scrapers/ak.py,sha256=h7BYMTV0whwWAPhbzVDVKMMoVCFphKly70aiTHabPq4,1847
|
|
16
|
-
warn/scrapers/al.py,sha256=
|
|
16
|
+
warn/scrapers/al.py,sha256=3_OtgUp4K6OL6H63SdtLQWE998r8PQpTNOv6PuiJEEc,1888
|
|
17
17
|
warn/scrapers/az.py,sha256=elGbue01Gjf_DQ66Wy9qqGIOJsiY-KIKJOVeft8pCXg,1447
|
|
18
18
|
warn/scrapers/ca.py,sha256=VQOfjHXPCc-jYwh-EPGVVfnzvXB7pdmCt2uJ6QnMPRM,8600
|
|
19
19
|
warn/scrapers/co.py,sha256=83OdikIrWGxt22mlI-_zLSNqJg1NO5C2Xjm3FF6DPYY,18252
|
|
@@ -54,9 +54,9 @@ warn/scrapers/va.py,sha256=7Nle7qL0VNPiE653XyaP9HQqSfuJFDRr2kEkjOqLvFM,11269
|
|
|
54
54
|
warn/scrapers/vt.py,sha256=d-bo4WK2hkrk4BhCCmLpEovcoZltlvdIUB6O0uaMx5A,1186
|
|
55
55
|
warn/scrapers/wa.py,sha256=UXdVtHZo_a-XfoiyOooTRfTb9W3PErSZdKca6SRORgs,4282
|
|
56
56
|
warn/scrapers/wi.py,sha256=ClEzXkwZbop0W4fkQgsb5oHAPUrb4luUPGV-jOKwkcg,4855
|
|
57
|
-
warn_scraper-1.2.
|
|
58
|
-
warn_scraper-1.2.
|
|
59
|
-
warn_scraper-1.2.
|
|
60
|
-
warn_scraper-1.2.
|
|
61
|
-
warn_scraper-1.2.
|
|
62
|
-
warn_scraper-1.2.
|
|
57
|
+
warn_scraper-1.2.157.dev0.dist-info/licenses/LICENSE,sha256=ZV-QHyqPwyMuwuj0lI05JeSjV1NyzVEk8Yeu7FPtYS0,585
|
|
58
|
+
warn_scraper-1.2.157.dev0.dist-info/METADATA,sha256=b5BB7-TNVZCGVMlKhEBamlTc9lwjcnNLYlsBG5UrgKw,1780
|
|
59
|
+
warn_scraper-1.2.157.dev0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
60
|
+
warn_scraper-1.2.157.dev0.dist-info/entry_points.txt,sha256=poh_oSweObGlBSs1_2qZmnTodlOYD0KfO7-h7W2UQIw,47
|
|
61
|
+
warn_scraper-1.2.157.dev0.dist-info/top_level.txt,sha256=dZfms6N3kqVXufiPOo7YqOrAcUtYfNH_oyGvYUk9FB4,5
|
|
62
|
+
warn_scraper-1.2.157.dev0.dist-info/RECORD,,
|
|
File without changes
|
{warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{warn_scraper-1.2.155.dev0.dist-info → warn_scraper-1.2.157.dev0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|