uk_bin_collection 0.114.6__py3-none-any.whl → 0.116.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- uk_bin_collection/tests/input.json +84 -21
- uk_bin_collection/uk_bin_collection/councils/AntrimAndNewtonabbeyCouncil.py +53 -0
- uk_bin_collection/uk_bin_collection/councils/ArgyllandButeCouncil.py +67 -0
- uk_bin_collection/uk_bin_collection/councils/AshfieldDistrictCouncil.py +105 -0
- uk_bin_collection/uk_bin_collection/councils/BaberghDistrictCouncil.py +132 -0
- uk_bin_collection/uk_bin_collection/councils/BradfordMDC.py +36 -6
- uk_bin_collection/uk_bin_collection/councils/BroxbourneCouncil.py +71 -0
- uk_bin_collection/uk_bin_collection/councils/CheshireWestAndChesterCouncil.py +95 -113
- uk_bin_collection/uk_bin_collection/councils/DerbyCityCouncil.py +55 -0
- uk_bin_collection/uk_bin_collection/councils/GraveshamBoroughCouncil.py +122 -0
- uk_bin_collection/uk_bin_collection/councils/HertsmereBoroughCouncil.py +161 -0
- uk_bin_collection/uk_bin_collection/councils/MidSuffolkDistrictCouncil.py +132 -0
- uk_bin_collection/uk_bin_collection/councils/MiltonKeynesCityCouncil.py +60 -41
- uk_bin_collection/uk_bin_collection/councils/WarringtonBoroughCouncil.py +50 -0
- uk_bin_collection/uk_bin_collection/councils/WestLancashireBoroughCouncil.py +114 -0
- {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/METADATA +1 -1
- {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/RECORD +20 -9
- {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/LICENSE +0 -0
- {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/WHEEL +0 -0
- {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/entry_points.txt +0 -0
@@ -12,6 +12,12 @@
|
|
12
12
|
"wiki_name": "Adur and Worthing Councils",
|
13
13
|
"wiki_note": "Replace XXXXXXXX with your UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find it."
|
14
14
|
},
|
15
|
+
"AntrimAndNewtonabbeyCouncil": {
|
16
|
+
"url": "https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule/?Id=643",
|
17
|
+
"wiki_command_url_override": "https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule/?Id=XXXX",
|
18
|
+
"wiki_name": "Antrim & Newtonabbey Council",
|
19
|
+
"wiki_note": "Navigate to [https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule] and search for your street name. Use the URL with the ID to replace XXXXXXXX with your specific ID."
|
20
|
+
},
|
15
21
|
"ArdsAndNorthDownCouncil": {
|
16
22
|
"url": "https://www.ardsandnorthdown.gov.uk",
|
17
23
|
"wiki_command_url_override": "https://www.ardsandnorthdown.gov.uk",
|
@@ -26,6 +32,13 @@
|
|
26
32
|
"wiki_name": "Ards and North Down Council",
|
27
33
|
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
28
34
|
},
|
35
|
+
"ArgyllandButeCouncil": {
|
36
|
+
"uprn": "125061759",
|
37
|
+
"skip_get_url": true,
|
38
|
+
"url": "https://www.argyll-bute.gov.uk",
|
39
|
+
"wiki_name": "Argyll and Bute Council",
|
40
|
+
"wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
41
|
+
},
|
29
42
|
"ArmaghBanbridgeCraigavonCouncil": {
|
30
43
|
"url": "https://www.armaghbanbridgecraigavon.gov.uk/",
|
31
44
|
"wiki_command_url_override": "https://www.armaghbanbridgecraigavon.gov.uk/",
|
@@ -42,13 +55,13 @@
|
|
42
55
|
"wiki_name": "Arun Council",
|
43
56
|
"wiki_note": "Pass the house name/number and postcode in their respective parameters, both wrapped in double quotes. This parser requires a Selenium webdriver."
|
44
57
|
},
|
45
|
-
"
|
46
|
-
"url": "https://
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
"wiki_name": "
|
51
|
-
"wiki_note": "
|
58
|
+
"AshfieldDistrictCouncil": {
|
59
|
+
"url": "https://www.ashfield.gov.uk",
|
60
|
+
"postcode": "NG16 6RH",
|
61
|
+
"house_number": "1",
|
62
|
+
"web_driver": "http://selenium:4444",
|
63
|
+
"wiki_name": "Ashfield District Council",
|
64
|
+
"wiki_note": "Pass the house name/number and postcode in their respective parameters, both wrapped in double quotes. This parser requires a Selenium webdriver"
|
52
65
|
},
|
53
66
|
"AshfordBoroughCouncil": {
|
54
67
|
"url": "https://ashford.gov.uk",
|
@@ -65,6 +78,13 @@
|
|
65
78
|
"wiki_name": "Aylesbury Vale Council (Buckinghamshire)",
|
66
79
|
"wiki_note": "To get the UPRN, please use [FindMyAddress](https://www.findmyaddress.co.uk/search). Returns all published collections in the past, present, future."
|
67
80
|
},
|
81
|
+
"BaberghDistrictCouncil": {
|
82
|
+
"skip_get_url": true,
|
83
|
+
"house_number": "Monday",
|
84
|
+
"url": "https://www.babergh.gov.uk",
|
85
|
+
"wiki_name": "Babergh District Council",
|
86
|
+
"wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday"
|
87
|
+
},
|
68
88
|
"BCPCouncil": {
|
69
89
|
"skip_get_url": true,
|
70
90
|
"uprn": "100040810214",
|
@@ -230,6 +250,13 @@
|
|
230
250
|
"wiki_name": "Bromsgrove District Council",
|
231
251
|
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
232
252
|
},
|
253
|
+
"BroxbourneCouncil": {
|
254
|
+
"url": "https://www.broxbourne.gov.uk",
|
255
|
+
"uprn": "148048608",
|
256
|
+
"postcode": "EN8 7FL",
|
257
|
+
"wiki_name": "Broxbourne Council",
|
258
|
+
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
259
|
+
},
|
233
260
|
"BroxtoweBoroughCouncil": {
|
234
261
|
"postcode": "NG16 2LY",
|
235
262
|
"skip_get_url": true,
|
@@ -322,26 +349,19 @@
|
|
322
349
|
"wiki_note": "Both the UPRN and a one-line address are passed in the URL, which needs to be wrapped in double quotes. The one-line address is made up of the house number, street name, and postcode. Use the form [here](https://online.cheshireeast.gov.uk/mycollectionday/) to find them, then take the first line and postcode and replace all spaces with `%20`."
|
323
350
|
},
|
324
351
|
"CheshireWestAndChesterCouncil": {
|
325
|
-
"
|
326
|
-
"postcode": "CH3 9ER",
|
352
|
+
"uprn": "100012346655",
|
327
353
|
"skip_get_url": true,
|
328
|
-
"url": "https://
|
354
|
+
"url": "https://my.cheshirewestandchester.gov.uk",
|
329
355
|
"wiki_name": "Cheshire West and Chester Council",
|
330
|
-
"wiki_note": "Pass the
|
356
|
+
"wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
331
357
|
},
|
332
358
|
"ChesterfieldBoroughCouncil": {
|
333
359
|
"uprn": "74008234",
|
334
360
|
"skip_get_url": true,
|
335
|
-
"url": "https://www.
|
361
|
+
"url": "https://www.chesterfield.gov.uk",
|
336
362
|
"wiki_name": "Chesterfield Borough Council",
|
337
363
|
"wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
338
364
|
},
|
339
|
-
"ChesterfieldBoroughCouncil": {
|
340
|
-
"uprn": "74008234",
|
341
|
-
"skip_get_url": true,
|
342
|
-
"url": "https://www.cheshirewestandchester.gov.uk/residents/waste-and-recycling/your-bin-collection/collection-day",
|
343
|
-
"wiki_name": "Chesterfield Borough Council"
|
344
|
-
},
|
345
365
|
"ChichesterDistrictCouncil": {
|
346
366
|
"house_number": "7, Plaistow Road, Kirdford, Billingshurst, West Sussex",
|
347
367
|
"postcode": "RH14 0JT",
|
@@ -436,6 +456,12 @@
|
|
436
456
|
"wiki_name": "Dartford Borough Council",
|
437
457
|
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
438
458
|
},
|
459
|
+
"DerbyCityCouncil": {
|
460
|
+
"url": "https://www.derby.gov.uk",
|
461
|
+
"uprn": "10010684240",
|
462
|
+
"wiki_name": "Derby City Council",
|
463
|
+
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
464
|
+
},
|
439
465
|
"DerbyshireDalesDistrictCouncil": {
|
440
466
|
"postcode": "DE4 3AS",
|
441
467
|
"skip_get_url": true,
|
@@ -689,6 +715,13 @@
|
|
689
715
|
"wiki_name": "Gloucester City Council",
|
690
716
|
"wiki_note": "Pass the house number, postcode, and UPRN in their respective parameters. This parser requires a Selenium webdriver."
|
691
717
|
},
|
718
|
+
"GraveshamBoroughCouncil": {
|
719
|
+
"uprn": "100060927046",
|
720
|
+
"skip_get_url": true,
|
721
|
+
"url": "https://www.gravesham.gov.uk",
|
722
|
+
"wiki_name": "Gravesham Borough Council",
|
723
|
+
"wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
724
|
+
},
|
692
725
|
"GuildfordCouncil": {
|
693
726
|
"house_number": "THE LODGE, PUTTENHAM HILL HOUSE, PUTTENHAM HILL, PUTTENHAM, GUILDFORD, GU3 1AH",
|
694
727
|
"postcode": "GU3 1AH",
|
@@ -736,6 +769,15 @@
|
|
736
769
|
"wiki_name": "Harrogate Borough Council",
|
737
770
|
"wiki_note": "Pass the UPRN, which can be found at [this site](https://secure.harrogate.gov.uk/inmyarea). URL doesn't need to be passed."
|
738
771
|
},
|
772
|
+
"HertsmereBoroughCouncil": {
|
773
|
+
"house_number": "1",
|
774
|
+
"postcode": "WD7 9HZ",
|
775
|
+
"skip_get_url": true,
|
776
|
+
"url": "https://www.hertsmere.gov.uk",
|
777
|
+
"web_driver": "http://selenium:4444",
|
778
|
+
"wiki_name": "Hertsmere Borough Council",
|
779
|
+
"wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
|
780
|
+
},
|
739
781
|
"HighlandCouncil": {
|
740
782
|
"url": "https://www.highland.gov.uk",
|
741
783
|
"wiki_command_url_override": "https://www.highland.gov.uk",
|
@@ -960,6 +1002,13 @@
|
|
960
1002
|
"wiki_name": "Midlothian Council",
|
961
1003
|
"wiki_note": "Pass the house name/number wrapped in double quotes along with the postcode parameter."
|
962
1004
|
},
|
1005
|
+
"MidSuffolkDistrictCouncil": {
|
1006
|
+
"skip_get_url": true,
|
1007
|
+
"house_number": "Monday",
|
1008
|
+
"url": "https://www.midsuffolk.gov.uk",
|
1009
|
+
"wiki_name": "Mid Suffolk District Council",
|
1010
|
+
"wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday"
|
1011
|
+
},
|
963
1012
|
"MidSussexDistrictCouncil": {
|
964
1013
|
"house_number": "OAKLANDS, OAKLANDS ROAD RH16 1SS",
|
965
1014
|
"postcode": "RH16 1SS",
|
@@ -970,10 +1019,10 @@
|
|
970
1019
|
"wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes. This parser requires a Selenium webdriver."
|
971
1020
|
},
|
972
1021
|
"MiltonKeynesCityCouncil": {
|
973
|
-
"uprn": "
|
974
|
-
"url": "https://
|
1022
|
+
"uprn": "25109551",
|
1023
|
+
"url": "https://mycouncil.milton-keynes.gov.uk/en/service/Waste_Collection_Round_Checker",
|
975
1024
|
"wiki_name": "Milton Keynes City Council",
|
976
|
-
"wiki_note": "
|
1025
|
+
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
977
1026
|
},
|
978
1027
|
"MoleValleyDistrictCouncil": {
|
979
1028
|
"postcode": "RH4 1SJ",
|
@@ -1664,6 +1713,13 @@
|
|
1664
1713
|
"wiki_name": "Waltham Forest",
|
1665
1714
|
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
1666
1715
|
},
|
1716
|
+
"WarringtonBoroughCouncil": {
|
1717
|
+
"url": "https://www.warrington.gov.uk",
|
1718
|
+
"wiki_command_url_override": "https://www.warrington.gov.uk",
|
1719
|
+
"uprn": "10094964379",
|
1720
|
+
"wiki_name": "Warrington Borough Council",
|
1721
|
+
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
1722
|
+
},
|
1667
1723
|
"WarwickDistrictCouncil": {
|
1668
1724
|
"url": "https://estates7.warwickdc.gov.uk/PropertyPortal/Property/Recycling/100070263793",
|
1669
1725
|
"wiki_command_url_override": "https://estates7.warwickdc.gov.uk/PropertyPortal/Property/Recycling/XXXXXXXX",
|
@@ -1715,6 +1771,13 @@
|
|
1715
1771
|
"wiki_name": "West Berkshire Council",
|
1716
1772
|
"wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
|
1717
1773
|
},
|
1774
|
+
"WestLancashireBoroughCouncil": {
|
1775
|
+
"url": "https://www.westlancs.gov.uk",
|
1776
|
+
"uprn": "10012343339",
|
1777
|
+
"postcode": "WN8 0HR",
|
1778
|
+
"wiki_name": "West Lancashire Borough Council",
|
1779
|
+
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
1780
|
+
},
|
1718
1781
|
"WestLindseyDistrictCouncil": {
|
1719
1782
|
"house_number": "PRIVATE ACCOMMODATION",
|
1720
1783
|
"postcode": "LN8 2AR",
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
|
3
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
4
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
5
|
+
|
6
|
+
|
7
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
8
|
+
class CouncilClass(AbstractGetBinDataClass):
|
9
|
+
"""
|
10
|
+
Concrete classes have to implement all abstract operations of the
|
11
|
+
base class. They can also override some operations with a default
|
12
|
+
implementation.
|
13
|
+
"""
|
14
|
+
|
15
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
16
|
+
|
17
|
+
bindata = {"bins": []}
|
18
|
+
|
19
|
+
soup = BeautifulSoup(page.content, "html.parser")
|
20
|
+
soup.prettify
|
21
|
+
|
22
|
+
collection_divs = soup.select("div.feature-box.bins")
|
23
|
+
if not collection_divs:
|
24
|
+
raise Exception("No collections found")
|
25
|
+
|
26
|
+
for collection_div in collection_divs:
|
27
|
+
date_p = collection_div.select_one("p.date")
|
28
|
+
if not date_p:
|
29
|
+
continue
|
30
|
+
|
31
|
+
# Thu 22 Aug, 2024
|
32
|
+
date_ = datetime.strptime(date_p.text.strip(), "%a %d %b, %Y").strftime(
|
33
|
+
"%d/%m/%Y"
|
34
|
+
)
|
35
|
+
bins = collection_div.select("li")
|
36
|
+
if not bins:
|
37
|
+
continue
|
38
|
+
for bin in bins:
|
39
|
+
if not bin.text.strip():
|
40
|
+
continue
|
41
|
+
bin_type = bin.text.strip()
|
42
|
+
|
43
|
+
dict_data = {
|
44
|
+
"type": bin_type,
|
45
|
+
"collectionDate": date_,
|
46
|
+
}
|
47
|
+
bindata["bins"].append(dict_data)
|
48
|
+
|
49
|
+
bindata["bins"].sort(
|
50
|
+
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
|
51
|
+
)
|
52
|
+
|
53
|
+
return bindata
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
import requests
|
4
|
+
from bs4 import BeautifulSoup
|
5
|
+
|
6
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
7
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
8
|
+
|
9
|
+
|
10
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
11
|
+
class CouncilClass(AbstractGetBinDataClass):
|
12
|
+
"""
|
13
|
+
Concrete classes have to implement all abstract operations of the
|
14
|
+
base class. They can also override some operations with a default
|
15
|
+
implementation.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
19
|
+
|
20
|
+
user_uprn = kwargs.get("uprn")
|
21
|
+
check_uprn(user_uprn)
|
22
|
+
user_uprn = user_uprn.zfill(12)
|
23
|
+
bindata = {"bins": []}
|
24
|
+
|
25
|
+
URI = "https://www.argyll-bute.gov.uk/rubbish-and-recycling/household-waste/bin-collection"
|
26
|
+
|
27
|
+
data = {"addressSelect": user_uprn}
|
28
|
+
|
29
|
+
s = requests.session()
|
30
|
+
r = s.post(URI, data=data)
|
31
|
+
r.raise_for_status()
|
32
|
+
|
33
|
+
soup = BeautifulSoup(r.content, features="html.parser")
|
34
|
+
soup.prettify()
|
35
|
+
|
36
|
+
# Find the table and extract the rows with bin schedule information
|
37
|
+
table = soup.find("table", class_="table table-bordered")
|
38
|
+
rows = table.find_all("tr")[1:] # Skip the header row
|
39
|
+
|
40
|
+
current_year = datetime.now().year
|
41
|
+
# Loop through each row and extract the bin type and collection date
|
42
|
+
for row in rows:
|
43
|
+
cells = row.find_all("td")
|
44
|
+
bin_type = cells[0].get_text(strip=True)
|
45
|
+
collection_date = cells[1].get_text(strip=True)
|
46
|
+
|
47
|
+
collection_date = datetime.strptime(
|
48
|
+
collection_date,
|
49
|
+
"%A %d %B",
|
50
|
+
)
|
51
|
+
|
52
|
+
if collection_date.month == 1:
|
53
|
+
collection_date = collection_date.replace(year=current_year + 1)
|
54
|
+
else:
|
55
|
+
collection_date = collection_date.replace(year=current_year)
|
56
|
+
|
57
|
+
dict_data = {
|
58
|
+
"type": bin_type,
|
59
|
+
"collectionDate": collection_date.strftime(date_format),
|
60
|
+
}
|
61
|
+
bindata["bins"].append(dict_data)
|
62
|
+
|
63
|
+
bindata["bins"].sort(
|
64
|
+
key=lambda x: datetime.strptime(x.get("collectionDate"), date_format)
|
65
|
+
)
|
66
|
+
|
67
|
+
return bindata
|
@@ -0,0 +1,105 @@
|
|
1
|
+
import time
|
2
|
+
from datetime import datetime
|
3
|
+
|
4
|
+
from bs4 import BeautifulSoup
|
5
|
+
from selenium import webdriver
|
6
|
+
from selenium.webdriver.common.by import By
|
7
|
+
from selenium.webdriver.support import expected_conditions as EC
|
8
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
9
|
+
|
10
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
11
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
12
|
+
|
13
|
+
|
14
|
+
class CouncilClass(AbstractGetBinDataClass):
|
15
|
+
"""
|
16
|
+
Concrete classes have to implement all abstract operations of the
|
17
|
+
base class. They can also override some operations with a default
|
18
|
+
implementation.
|
19
|
+
"""
|
20
|
+
|
21
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
22
|
+
# Get and check UPRN
|
23
|
+
user_postcode = kwargs.get("postcode")
|
24
|
+
user_paon = kwargs.get("paon")
|
25
|
+
check_paon(user_paon)
|
26
|
+
check_postcode(user_postcode)
|
27
|
+
web_driver = kwargs.get("web_driver")
|
28
|
+
headless = kwargs.get("headless")
|
29
|
+
bindata = {"bins": []}
|
30
|
+
|
31
|
+
API_URL = "https://portal.digital.ashfield.gov.uk/w/webpage/raise-case?service=bin_calendar"
|
32
|
+
|
33
|
+
# Create Selenium webdriver
|
34
|
+
driver = create_webdriver(web_driver, headless, None, __name__)
|
35
|
+
driver.get(API_URL)
|
36
|
+
|
37
|
+
title = WebDriverWait(driver, 10).until(
|
38
|
+
EC.presence_of_element_located((By.ID, "sub_page_title"))
|
39
|
+
)
|
40
|
+
|
41
|
+
# Wait for the postcode field to appear then populate it
|
42
|
+
WebDriverWait(driver, 10).until(
|
43
|
+
EC.presence_of_element_located(
|
44
|
+
(By.CSS_SELECTOR, "input.relation_path_type_ahead_search")
|
45
|
+
)
|
46
|
+
)
|
47
|
+
|
48
|
+
inputElement_postcode = WebDriverWait(driver, 10).until(
|
49
|
+
EC.presence_of_element_located(
|
50
|
+
(By.CSS_SELECTOR, "input.relation_path_type_ahead_search")
|
51
|
+
)
|
52
|
+
)
|
53
|
+
inputElement_postcode.clear()
|
54
|
+
inputElement_postcode.send_keys(user_postcode)
|
55
|
+
|
56
|
+
# Wait for the 'Select your property' dropdown to appear and select the first result
|
57
|
+
dropdown = WebDriverWait(driver, 10).until(
|
58
|
+
EC.element_to_be_clickable(
|
59
|
+
(
|
60
|
+
By.CLASS_NAME,
|
61
|
+
"result_list ",
|
62
|
+
)
|
63
|
+
)
|
64
|
+
)
|
65
|
+
|
66
|
+
address_element = (
|
67
|
+
WebDriverWait(driver, 10)
|
68
|
+
.until(
|
69
|
+
EC.element_to_be_clickable(
|
70
|
+
(By.XPATH, f"//li[starts-with(@aria-label, '{user_paon}')]")
|
71
|
+
)
|
72
|
+
)
|
73
|
+
.click()
|
74
|
+
)
|
75
|
+
|
76
|
+
search_button = WebDriverWait(driver, 10).until(
|
77
|
+
EC.element_to_be_clickable(
|
78
|
+
(By.XPATH, "//input[@type='submit' and @value='Search']")
|
79
|
+
)
|
80
|
+
)
|
81
|
+
search_button.click()
|
82
|
+
|
83
|
+
time.sleep(10)
|
84
|
+
|
85
|
+
soup = BeautifulSoup(driver.page_source, features="html.parser")
|
86
|
+
soup.prettify()
|
87
|
+
|
88
|
+
# Find the table by class name
|
89
|
+
table = soup.find("table", {"class": "table listing table-striped"})
|
90
|
+
|
91
|
+
# Iterate over each row in the tbody of the table
|
92
|
+
for row in table.find("tbody").find_all("tr"):
|
93
|
+
# Extract the service, day, and date for each row
|
94
|
+
service = row.find_all("td")[0].get_text(strip=True)
|
95
|
+
date = row.find_all("td")[2].get_text(strip=True)
|
96
|
+
|
97
|
+
dict_data = {
|
98
|
+
"type": service,
|
99
|
+
"collectionDate": datetime.strptime(date, "%a, %d %b %Y").strftime(
|
100
|
+
date_format
|
101
|
+
),
|
102
|
+
}
|
103
|
+
bindata["bins"].append(dict_data)
|
104
|
+
|
105
|
+
return bindata
|
@@ -0,0 +1,132 @@
|
|
1
|
+
import re
|
2
|
+
import time
|
3
|
+
|
4
|
+
import requests
|
5
|
+
from bs4 import BeautifulSoup
|
6
|
+
from selenium.webdriver.common.by import By
|
7
|
+
from selenium.webdriver.support import expected_conditions as EC
|
8
|
+
from selenium.webdriver.support.ui import Select
|
9
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
10
|
+
|
11
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
12
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
13
|
+
|
14
|
+
|
15
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
16
|
+
class CouncilClass(AbstractGetBinDataClass):
|
17
|
+
"""
|
18
|
+
Concrete classes have to implement all abstract operations of the
|
19
|
+
base class. They can also override some operations with a default
|
20
|
+
implementation.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
24
|
+
|
25
|
+
collection_day = kwargs.get("paon")
|
26
|
+
bindata = {"bins": []}
|
27
|
+
|
28
|
+
days_of_week = [
|
29
|
+
"Monday",
|
30
|
+
"Tuesday",
|
31
|
+
"Wednesday",
|
32
|
+
"Thursday",
|
33
|
+
"Friday",
|
34
|
+
"Saturday",
|
35
|
+
"Sunday",
|
36
|
+
]
|
37
|
+
|
38
|
+
refusestartDate = datetime(2024, 11, 4)
|
39
|
+
recyclingstartDate = datetime(2024, 11, 11)
|
40
|
+
|
41
|
+
offset_days = days_of_week.index(collection_day)
|
42
|
+
|
43
|
+
refuse_dates = get_dates_every_x_days(refusestartDate, 14, 28)
|
44
|
+
recycling_dates = get_dates_every_x_days(recyclingstartDate, 14, 28)
|
45
|
+
|
46
|
+
bank_holidays = [
|
47
|
+
("25/12/2024", 2),
|
48
|
+
("26/12/2024", 2),
|
49
|
+
("27/12/2024", 3),
|
50
|
+
("30/12/2024", 1),
|
51
|
+
("31/12/2024", 2),
|
52
|
+
("01/01/2025", 2),
|
53
|
+
("02/01/2025", 2),
|
54
|
+
("03/01/2025", 3),
|
55
|
+
("06/01/2025", 1),
|
56
|
+
("07/01/2025", 1),
|
57
|
+
("08/01/2025", 1),
|
58
|
+
("09/01/2025", 1),
|
59
|
+
("10/01/2025", 1),
|
60
|
+
("18/04/2025", 1),
|
61
|
+
("21/04/2025", 1),
|
62
|
+
("22/04/2025", 1),
|
63
|
+
("23/04/2025", 1),
|
64
|
+
("24/04/2025", 1),
|
65
|
+
("25/04/2025", 1),
|
66
|
+
("05/05/2025", 1),
|
67
|
+
("06/05/2025", 1),
|
68
|
+
("07/05/2025", 1),
|
69
|
+
("08/05/2025", 1),
|
70
|
+
("09/05/2025", 1),
|
71
|
+
("26/05/2025", 1),
|
72
|
+
("27/05/2025", 1),
|
73
|
+
("28/05/2025", 1),
|
74
|
+
("29/05/2025", 1),
|
75
|
+
("30/05/2025", 1),
|
76
|
+
("25/08/2025", 1),
|
77
|
+
("26/08/2025", 1),
|
78
|
+
("27/08/2025", 1),
|
79
|
+
("28/08/2025", 1),
|
80
|
+
("29/08/2025", 1),
|
81
|
+
]
|
82
|
+
|
83
|
+
for refuseDate in refuse_dates:
|
84
|
+
|
85
|
+
collection_date = (
|
86
|
+
datetime.strptime(refuseDate, "%d/%m/%Y") + timedelta(days=offset_days)
|
87
|
+
).strftime("%d/%m/%Y")
|
88
|
+
|
89
|
+
holiday_offset = next(
|
90
|
+
(value for date, value in bank_holidays if date == collection_date), 0
|
91
|
+
)
|
92
|
+
|
93
|
+
if holiday_offset > 0:
|
94
|
+
collection_date = (
|
95
|
+
datetime.strptime(collection_date, "%d/%m/%Y")
|
96
|
+
+ timedelta(days=holiday_offset)
|
97
|
+
).strftime("%d/%m/%Y")
|
98
|
+
|
99
|
+
dict_data = {
|
100
|
+
"type": "Refuse Bin",
|
101
|
+
"collectionDate": collection_date,
|
102
|
+
}
|
103
|
+
bindata["bins"].append(dict_data)
|
104
|
+
|
105
|
+
for recyclingDate in recycling_dates:
|
106
|
+
|
107
|
+
collection_date = (
|
108
|
+
datetime.strptime(recyclingDate, "%d/%m/%Y")
|
109
|
+
+ timedelta(days=offset_days)
|
110
|
+
).strftime("%d/%m/%Y")
|
111
|
+
|
112
|
+
holiday_offset = next(
|
113
|
+
(value for date, value in bank_holidays if date == collection_date), 0
|
114
|
+
)
|
115
|
+
|
116
|
+
if holiday_offset > 0:
|
117
|
+
collection_date = (
|
118
|
+
datetime.strptime(collection_date, "%d/%m/%Y")
|
119
|
+
+ timedelta(days=holiday_offset)
|
120
|
+
).strftime("%d/%m/%Y")
|
121
|
+
|
122
|
+
dict_data = {
|
123
|
+
"type": "Recycling Bin",
|
124
|
+
"collectionDate": collection_date,
|
125
|
+
}
|
126
|
+
bindata["bins"].append(dict_data)
|
127
|
+
|
128
|
+
bindata["bins"].sort(
|
129
|
+
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
|
130
|
+
)
|
131
|
+
|
132
|
+
return bindata
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import re
|
2
|
+
|
1
3
|
import requests
|
2
4
|
from bs4 import BeautifulSoup
|
3
5
|
|
@@ -89,12 +91,40 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
89
91
|
)
|
90
92
|
).strftime(date_format)
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
94
|
+
# Build data dict for each entry
|
95
|
+
dict_data = {
|
96
|
+
"type": bin_type,
|
97
|
+
"collectionDate": bin_date,
|
98
|
+
}
|
99
|
+
data["bins"].append(dict_data)
|
100
|
+
|
101
|
+
for bin in soup.find_all(attrs={"id": re.compile(r"CTID-D0TUYGxO-\d+-A")}):
|
102
|
+
dict_data = {
|
103
|
+
"type": "General Waste",
|
104
|
+
"collectionDate": datetime.strptime(
|
105
|
+
bin.text.strip(),
|
106
|
+
"%a %b %d %Y",
|
107
|
+
).strftime(date_format),
|
108
|
+
}
|
109
|
+
data["bins"].append(dict_data)
|
110
|
+
for bin in soup.find_all(attrs={"id": re.compile(r"CTID-d3gapLk-\d+-A")}):
|
111
|
+
dict_data = {
|
112
|
+
"type": "Recycling Waste",
|
113
|
+
"collectionDate": datetime.strptime(
|
114
|
+
bin.text.strip(),
|
115
|
+
"%a %b %d %Y",
|
116
|
+
).strftime(date_format),
|
117
|
+
}
|
118
|
+
data["bins"].append(dict_data)
|
119
|
+
for bin in soup.find_all(attrs={"id": re.compile(r"CTID-L8OidMPA-\d+-A")}):
|
120
|
+
dict_data = {
|
121
|
+
"type": "Garden Waste (Subscription Only)",
|
122
|
+
"collectionDate": datetime.strptime(
|
123
|
+
bin.text.strip(),
|
124
|
+
"%a %b %d %Y",
|
125
|
+
).strftime(date_format),
|
126
|
+
}
|
127
|
+
data["bins"].append(dict_data)
|
98
128
|
|
99
129
|
data["bins"].sort(
|
100
130
|
key=lambda x: datetime.strptime(x.get("collectionDate"), date_format)
|