warn-scraper 1.2.167.dev0__py3-none-any.whl → 1.2.169.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/nd.py +96 -0
- warn/scrapers/tx.py +1 -0
- {warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/METADATA +1 -1
- {warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/RECORD +10 -9
- {warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/scm_file_list.json +1 -0
- warn_scraper-1.2.169.dev0.dist-info/scm_version.json +8 -0
- warn_scraper-1.2.167.dev0.dist-info/scm_version.json +0 -8
- {warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/WHEEL +0 -0
- {warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/entry_points.txt +0 -0
- {warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/licenses/LICENSE +0 -0
- {warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/top_level.txt +0 -0
warn/scrapers/nd.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pdfplumber
|
|
6
|
+
|
|
7
|
+
from .. import utils
|
|
8
|
+
from ..cache import Cache
|
|
9
|
+
|
|
10
|
+
__authors__ = ["riordan"]
|
|
11
|
+
__tags__ = ["pdf"]
|
|
12
|
+
__source__ = {
|
|
13
|
+
"name": "Job Service North Dakota",
|
|
14
|
+
"url": "https://www.jobsnd.com/documents",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def scrape(
|
|
21
|
+
data_dir: Path = utils.WARN_DATA_DIR,
|
|
22
|
+
cache_dir: Path = utils.WARN_CACHE_DIR,
|
|
23
|
+
) -> Path:
|
|
24
|
+
"""
|
|
25
|
+
Scrape data from North Dakota.
|
|
26
|
+
|
|
27
|
+
Keyword arguments:
|
|
28
|
+
data_dir -- the Path were the result will be saved (default WARN_DATA_DIR)
|
|
29
|
+
cache_dir -- the Path where results can be cached (default WARN_CACHE_DIR)
|
|
30
|
+
|
|
31
|
+
Returns: the Path where the file is written
|
|
32
|
+
"""
|
|
33
|
+
# Fire up the cache
|
|
34
|
+
cache = Cache(cache_dir)
|
|
35
|
+
state_code = "nd"
|
|
36
|
+
|
|
37
|
+
# North Dakota publishes a single static PDF covering 2015 to the present.
|
|
38
|
+
pdf_url = "https://www.jobsnd.com/sites/www/files/documents/jsnd-documents/WARN%20Notices%202015%20to%20present.pdf"
|
|
39
|
+
|
|
40
|
+
# Download the PDF and stash the raw file, unedited, in the cache
|
|
41
|
+
cache_key = f"{state_code}/WARN_Notices_2015_to_present.pdf"
|
|
42
|
+
pdf_path = cache.download(cache_key, pdf_url)
|
|
43
|
+
|
|
44
|
+
# Loop through the PDF pages and pull out the table
|
|
45
|
+
output_rows: list = []
|
|
46
|
+
header_written = False
|
|
47
|
+
with pdfplumber.open(pdf_path) as pdf:
|
|
48
|
+
for page_index, page in enumerate(pdf.pages):
|
|
49
|
+
rows = page.extract_table() or []
|
|
50
|
+
|
|
51
|
+
for row in rows:
|
|
52
|
+
# Standardize each cell
|
|
53
|
+
output_row = [_clean_text(cell) for cell in row]
|
|
54
|
+
|
|
55
|
+
# Skip fully empty rows
|
|
56
|
+
if not any(output_row):
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
# The header repeats at the top of the table. Keep it once.
|
|
60
|
+
if output_row[0] == "Company Name":
|
|
61
|
+
if header_written:
|
|
62
|
+
logger.debug(
|
|
63
|
+
f"Skipping repeated header row on page {page_index + 1}"
|
|
64
|
+
)
|
|
65
|
+
continue
|
|
66
|
+
header_written = True
|
|
67
|
+
|
|
68
|
+
output_rows.append(output_row)
|
|
69
|
+
|
|
70
|
+
# Write out to CSV
|
|
71
|
+
data_path = data_dir / f"{state_code}.csv"
|
|
72
|
+
utils.write_rows_to_csv(data_path, output_rows)
|
|
73
|
+
|
|
74
|
+
# Return the path
|
|
75
|
+
return data_path
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _clean_text(text: str) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Clean up text from a PDF cell.
|
|
81
|
+
|
|
82
|
+
Keyword arguments:
|
|
83
|
+
text -- the text to clean
|
|
84
|
+
|
|
85
|
+
Returns: the cleaned text
|
|
86
|
+
"""
|
|
87
|
+
# Replace None with an empty string
|
|
88
|
+
if text is None:
|
|
89
|
+
return ""
|
|
90
|
+
|
|
91
|
+
# Collapse newlines and standardize whitespace
|
|
92
|
+
return re.sub(r"\s+", " ", text).strip()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
scrape()
|
warn/scrapers/tx.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: warn-scraper
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.169.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
|
|
@@ -36,6 +36,7 @@ warn/scrapers/mi.py,sha256=Ppyawp4nbzSBODuzDKeqnO9_9do5MFwK4Y_f3uc6blE,5846
|
|
|
36
36
|
warn/scrapers/mo.py,sha256=wnnwQAiVPwuheMqptMXZpyQdiKNghhKwTO-Bnh9oXoU,3492
|
|
37
37
|
warn/scrapers/ms.py,sha256=BZZoMw3TNtwzBeBsqKLsPznBcDjPaO49I5-yBU0e9AI,5502
|
|
38
38
|
warn/scrapers/mt.py,sha256=t2MP4OCcuCEnrnvNgOu289P0eekZq4XaCK65qzgZX88,2457
|
|
39
|
+
warn/scrapers/nd.py,sha256=x-mLrysn-Ns3tXg6MmjUYZ-4T0x6YUix_fuIldO5Dk0,2657
|
|
39
40
|
warn/scrapers/ne.py,sha256=JawuGJ3tCKvMd-N-p03gnltB4rol4QUJshMk2oyMPO4,4143
|
|
40
41
|
warn/scrapers/nj.py,sha256=nwbMbeQuUJbYRVoyUyKZBmNqvqsXu3Habt-10r8DvZE,2230
|
|
41
42
|
warn/scrapers/nm.py,sha256=55kDh65uOc-uMO8wuxPHhHN4Aw8Di2Fx66JBds8f2Io,3694
|
|
@@ -48,17 +49,17 @@ warn/scrapers/ri.py,sha256=EUyLy59eNiYHqiJR8C0YcJrZtp09KyVc45AFD0_Uc0U,4497
|
|
|
48
49
|
warn/scrapers/sc.py,sha256=p3kscSNSW9C8C5QaSUbCAo6XibgB7G2iH6zaMH7Mnsc,4819
|
|
49
50
|
warn/scrapers/sd.py,sha256=_4R19Ybzsyx1PvcWV3_laJmJ3etrwVGfhNEQm6njwoA,1904
|
|
50
51
|
warn/scrapers/tn.py,sha256=i1H7c09Ea3CDrTXqqRMLBMPT_34QtGA0-x7T8rm_j5Q,2945
|
|
51
|
-
warn/scrapers/tx.py,sha256=
|
|
52
|
+
warn/scrapers/tx.py,sha256=b9CWn9erFbYFvTacHUy7x-2YfjSuf_VGyt2bUlwq0c8,5668
|
|
52
53
|
warn/scrapers/ut.py,sha256=iUh38YIjbvv5MyyKacsiZNe8KjfdBeDaOf-qMQEF_kc,2245
|
|
53
54
|
warn/scrapers/va.py,sha256=7Nle7qL0VNPiE653XyaP9HQqSfuJFDRr2kEkjOqLvFM,11269
|
|
54
55
|
warn/scrapers/vt.py,sha256=d-bo4WK2hkrk4BhCCmLpEovcoZltlvdIUB6O0uaMx5A,1186
|
|
55
56
|
warn/scrapers/wa.py,sha256=UXdVtHZo_a-XfoiyOooTRfTb9W3PErSZdKca6SRORgs,4282
|
|
56
57
|
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.
|
|
63
|
-
warn_scraper-1.2.
|
|
64
|
-
warn_scraper-1.2.
|
|
58
|
+
warn_scraper-1.2.169.dev0.dist-info/licenses/LICENSE,sha256=ZV-QHyqPwyMuwuj0lI05JeSjV1NyzVEk8Yeu7FPtYS0,585
|
|
59
|
+
warn_scraper-1.2.169.dev0.dist-info/METADATA,sha256=idfAf3i-wQj6UbjYHXsG73B3pU8lvD4soFhnBis-e5c,1780
|
|
60
|
+
warn_scraper-1.2.169.dev0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
61
|
+
warn_scraper-1.2.169.dev0.dist-info/entry_points.txt,sha256=poh_oSweObGlBSs1_2qZmnTodlOYD0KfO7-h7W2UQIw,47
|
|
62
|
+
warn_scraper-1.2.169.dev0.dist-info/scm_file_list.json,sha256=PsUNADDLfiY6WCIaKYrGF-hSOzV6HMBxtHeJPnHAOAo,4147
|
|
63
|
+
warn_scraper-1.2.169.dev0.dist-info/scm_version.json,sha256=4YY6UpShLyl1mHlnjJR2Fi1BirY30YZrm1c8Wg1OkHA,161
|
|
64
|
+
warn_scraper-1.2.169.dev0.dist-info/top_level.txt,sha256=dZfms6N3kqVXufiPOo7YqOrAcUtYfNH_oyGvYUk9FB4,5
|
|
65
|
+
warn_scraper-1.2.169.dev0.dist-info/RECORD,,
|
|
File without changes
|
{warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{warn_scraper-1.2.167.dev0.dist-info → warn_scraper-1.2.169.dev0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|