33import re
44import time
55from bs4 import BeautifulSoup
6- from datetime import datetime
6+ from datetime import datetime , timezone , timedelta
77
88# Tanggal yang dimasukkan ke csv.
99def get_raw_date ():
1010 return datetime .today ().strftime ("%Y-%m-%d" )
1111
1212# Membuat dictionary berisi judul dan link yang dibuat beberapa jam lalu atau kemarin.
1313def find_links (url ):
14+ print (f"Crawling URL: { url } " )
1415 response = requests .get (url )
1516 soup = BeautifulSoup (response .content , 'html.parser' )
1617
1718 divs = soup .find_all ('div' , class_ = 'col-md-8' )
1819
1920 span_elements = []
2021
22+ # Mengambil waktu saat ini dalam zona waktu Jakarta (GMT+7)
23+ current_hour = datetime .now (timezone (timedelta (hours = 7 ))).hour
24+ # Menghitung jam yang sudah berlalu dari jam saat ini untuk crawl dari awal hari ini.
25+ hour_patterns = [f"\\ b{ i } jam lalu\\ b" for i in range (1 , current_hour + 1 )]
26+ minute_pattern = "\\ b\\ d+ menit lalu\\ b"
27+ time_pattern = f"{ minute_pattern } |{ ('|' .join (hour_patterns ))} "
28+
2129 for div in divs :
22- spans = div .find_all ('span' , string = re .compile (r'jam|kemarin' , re .IGNORECASE ))
30+ spans = div .find_all ('span' , string = re .compile (time_pattern , re .IGNORECASE ))
2331 span_elements .extend (spans )
2432
2533 links_dict = {}
2634 for span in span_elements :
2735 parent = span .find_parent ()
36+
37+ # Check if this span is related to content we want to filter out
38+ should_skip = False
39+ checking_element = span
40+ for _ in range (5 ): # Check up to 5 levels up to find slugs
41+ if not checking_element :
42+ break
43+
44+ # Check if there's a slug element
45+ slug_element = checking_element .find_previous ('h4' , class_ = 'slug' )
46+ if slug_element :
47+ slug_link = slug_element .find ('a' )
48+ if slug_link and 'video' in slug_link .text .lower ():
49+ should_skip = True
50+ break
51+
52+ checking_element = checking_element .parent
53+
54+ if should_skip :
55+ continue
56+
2857 while parent :
2958 link = parent .find ('a' , href = True )
3059 if link :
60+ # Skip link yang ada slug.
61+ if link .find_parent ('h4' , class_ = 'slug' ):
62+ break
63+
3164 title = link .get ('title' , '' )
32- links_dict [title ] = link ['href' ]
65+ if title : # Only add non-empty titles
66+ links_dict [title ] = link ['href' ]
3367 break
3468 parent = parent .find_parent ()
3569 return links_dict
@@ -38,7 +72,7 @@ def find_links(url):
3872def collect_articles (links_dict ):
3973 data = []
4074 raw_date = get_raw_date ()
41- source = "antara "
75+ source = "Antara "
4276 for title , link in links_dict .items ():
4377 try :
4478 get_content = requests .get (link , timeout = 10 )
@@ -58,13 +92,43 @@ def find_content(url):
5892 response = requests .get (url )
5993 soup = BeautifulSoup (response .content , 'html.parser' )
6094
61- # Extract image URL
95+ # Find the image with the largest area instead of by class
6296 image_url = ""
63- image_div = soup .find ('div' , class_ = 'wrap__article-detail-image' )
64- if image_div :
65- img_tag = image_div .find ('img' , class_ = 'img-fluid' )
66- if img_tag and img_tag .has_attr ('src' ):
67- image_url = img_tag ['src' ]
97+ max_area = 0
98+ for img in soup .find_all ('img' ):
99+ # Skip small icons, emoticons etc
100+ if not img .has_attr ('src' ) or img ['src' ].startswith ('data:' ):
101+ continue
102+
103+ # Get width and height attributes
104+ width_str = img .get ('width' , '0' )
105+ height_str = img .get ('height' , '0' )
106+
107+ # Remove 'px' suffix if present
108+ width_str = width_str .replace ('px' , '' ) if isinstance (width_str , str ) else width_str
109+ height_str = height_str .replace ('px' , '' ) if isinstance (height_str , str ) else height_str
110+
111+ try :
112+ width = int (width_str ) or int (re .search (r'width:\s*(\d+)px' , img .get ('style' , '' ) or '' ) or [0 , 0 ])[1 ]
113+ height = int (height_str ) or int (re .search (r'height:\s*(\d+)px' , img .get ('style' , '' ) or '' ) or [0 , 0 ])[1 ]
114+ except (ValueError , TypeError ):
115+ width , height = 0 , 0
116+
117+ # Calculate area
118+ area = width * height
119+
120+ # Update max area and image URL if this image is larger
121+ if area > max_area :
122+ max_area = area
123+ image_url = img ['src' ]
124+
125+ # If no area detected images, fallback to traditional method
126+ if not image_url :
127+ image_div = soup .find ('div' , class_ = 'wrap__article-detail-image mt-4' )
128+ if image_div :
129+ img_tag = image_div .find ('img' , class_ = 'img-fluid' )
130+ if img_tag and img_tag .has_attr ('src' ):
131+ image_url = img_tag ['src' ]
68132
69133 parent_p_count = {}
70134 # Mencari semua parent yang memiliki p. Jumlah p yang tertinggi = main content yang ingin diambil.
@@ -104,9 +168,41 @@ def extract_content(text):
104168 return match .group (2 ).strip () if match else text
105169
106170def crawl_antara ():
107- url = 'https://www.antaranews.com/tag/cek-fakta/'
108- links_dict = find_links (url )
109- data = collect_articles (links_dict )
171+ all_links = {}
172+ page = 1
173+ max_pages = 10 # Set a reasonable maximum to avoid infinite crawling
174+ consecutive_empty_pages = 0
175+
176+ while page <= max_pages :
177+ url = f'https://www.antaranews.com/terkini/{ page } '
178+ try :
179+ links_dict = find_links (url )
180+
181+ # If no articles found or we got an empty dictionary
182+ if not links_dict :
183+ consecutive_empty_pages += 1
184+ print (f"No articles found on page { page } " )
185+
186+ # Stop after 2 consecutive empty pages
187+ if consecutive_empty_pages >= 2 :
188+ print ("Reached end of articles, stopping crawler" )
189+ break
190+ else :
191+ consecutive_empty_pages = 0 # Reset counter when we find articles
192+ all_links .update (links_dict )
193+ print (f"Found { len (links_dict )} articles on page { page } " )
194+
195+ # Print each article title and link
196+ for title , link in links_dict .items ():
197+ print (f" - { title } : { link } " )
198+
199+ page += 1
200+ except requests .exceptions .RequestException as e :
201+ print (f"Error accessing page { page } : { e } " )
202+ break
203+
204+ # Process all collected links
205+ data = collect_articles (all_links )
110206
111207 # Format data for return
112208 formatted_data = []
@@ -126,4 +222,8 @@ def main():
126222 return crawl_antara ()
127223
128224if __name__ == "__main__" :
129- main ()
225+ articles = main ()
226+ titles = [article ['title' ] for article in articles ]
227+ print (titles )
228+
229+
0 commit comments