SonFox2920 commited on
Commit
79ff5a8
·
verified ·
1 Parent(s): 44d6c40

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +285 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ from bs4 import BeautifulSoup
4
+ import pandas as pd
5
+ import datetime
6
+ import csv
7
+ import os
8
+
9
+ st.set_page_config(page_title="VnExpress Crawler", page_icon="📰", layout="wide")
10
+
11
+ def get_article_details(url):
12
+ try:
13
+ response = requests.get(url)
14
+ soup = BeautifulSoup(response.content, 'html.parser')
15
+
16
+ # Lấy tiêu đề
17
+ title = soup.find('h1', class_='title-detail').text.strip() if soup.find('h1', class_='title-detail') else "Không có tiêu đề"
18
+
19
+ # Lấy mô tả
20
+ description = soup.find('p', class_='description').text.strip() if soup.find('p', class_='description') else "Không có mô tả"
21
+
22
+ # Lấy thời gian đăng
23
+ publish_time = ""
24
+ time_tag = soup.find('span', class_='date')
25
+ if time_tag:
26
+ publish_time = time_tag.text.strip()
27
+
28
+ # Lấy nội dung bài viết
29
+ content_div = soup.find('article', class_='fck_detail')
30
+ content = ""
31
+ if content_div:
32
+ paragraphs = content_div.find_all('p', class_='Normal')
33
+ content = " ".join([p.text.strip() for p in paragraphs])
34
+
35
+ # Lấy chuyên mục
36
+ category = ""
37
+ breadcrumb = soup.find('ul', class_='breadcrumb')
38
+ if breadcrumb:
39
+ category_item = breadcrumb.find_all('li')
40
+ if len(category_item) > 1:
41
+ category = category_item[1].text.strip()
42
+
43
+ return {
44
+ 'url': url,
45
+ 'title': title,
46
+ 'description': description,
47
+ 'publish_time': publish_time,
48
+ 'content': content,
49
+ 'category': category
50
+ }
51
+ except Exception as e:
52
+ st.error(f"Lỗi khi crawl bài viết {url}: {str(e)}")
53
+ return None
54
+
55
+ def get_articles_from_section(section_url, limit=10):
56
+ articles = []
57
+ try:
58
+ response = requests.get(section_url)
59
+ soup = BeautifulSoup(response.content, 'html.parser')
60
+
61
+ # Tìm các bài viết trong trang
62
+ article_items = soup.find_all('article', class_='item-news')
63
+
64
+ count = 0
65
+ for item in article_items:
66
+ if count >= limit:
67
+ break
68
+
69
+ title_tag = item.find('h3', class_='title-news')
70
+ if title_tag and title_tag.a:
71
+ article_url = title_tag.a['href']
72
+ if not article_url.startswith('http'):
73
+ article_url = 'https://vnexpress.net' + article_url
74
+
75
+ articles.append(article_url)
76
+ count += 1
77
+
78
+ except Exception as e:
79
+ st.error(f"Lỗi khi lấy danh sách bài viết: {str(e)}")
80
+
81
+ return articles
82
+
83
+ def save_to_csv(data, filename):
84
+ try:
85
+ with open(filename, 'w', newline='', encoding='utf-8-sig') as csvfile:
86
+ if not data:
87
+ st.error("Không có dữ liệu để lưu")
88
+ return False
89
+
90
+ fieldnames = data[0].keys()
91
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
92
+ writer.writeheader()
93
+ writer.writerows(data)
94
+ return True
95
+ except Exception as e:
96
+ st.error(f"Lỗi khi lưu file CSV: {str(e)}")
97
+ return False
98
+
99
+ # UI Streamlit
100
+ st.title("🗞️ Công cụ Crawl dữ liệu VnExpress")
101
+
102
+ st.markdown("""
103
+ Ứng dụng này giúp bạn crawl dữ liệu từ VnExpress và lưu thành file CSV.
104
+ """)
105
+
106
+ # Thanh tab để lựa chọn chế độ crawl
107
+ tab1, tab2 = st.tabs(["Crawl theo chuyên mục", "Crawl theo URL"])
108
+
109
+ with tab1:
110
+ st.header("Crawl theo chuyên mục")
111
+
112
+ # Danh sách các chuyên mục của VnExpress
113
+ sections = {
114
+ "Thời sự": "https://vnexpress.net/thoi-su",
115
+ "Thế giới": "https://vnexpress.net/the-gioi",
116
+ "Kinh doanh": "https://vnexpress.net/kinh-doanh",
117
+ "Khoa học": "https://vnexpress.net/khoa-hoc",
118
+ "Giải trí": "https://vnexpress.net/giai-tri",
119
+ "Thể thao": "https://vnexpress.net/the-thao",
120
+ "Pháp luật": "https://vnexpress.net/phap-luat",
121
+ "Giáo dục": "https://vnexpress.net/giao-duc",
122
+ "Sức khỏe": "https://vnexpress.net/suc-khoe",
123
+ "Đời sống": "https://vnexpress.net/doi-song",
124
+ "Du lịch": "https://vnexpress.net/du-lich",
125
+ "Số hóa": "https://vnexpress.net/so-hoa"
126
+ }
127
+
128
+ # Thêm tùy chọn "Tất cả" vào danh sách chuyên mục
129
+ section_options = ["Tất cả"] + list(sections.keys())
130
+ selected_section = st.selectbox("Chọn chuyên mục:", section_options)
131
+ num_articles = st.number_input("Số lượng bài viết cần crawl (mỗi chuyên mục nếu chọn Tất cả):", min_value=1, max_value=50, value=5)
132
+
133
+ if st.button("Bắt đầu crawl theo chuyên mục"):
134
+ article_data = []
135
+
136
+ if selected_section == "Tất cả":
137
+ # Crawl tất cả các chuyên mục
138
+ with st.spinner("Đang crawl dữ liệu từ tất cả các chuyên mục..."):
139
+ total_sections = len(sections)
140
+ main_progress = st.progress(0)
141
+
142
+ for i, (section_name, section_url) in enumerate(sections.items()):
143
+ st.write(f"Đang crawl chuyên mục {section_name} ({i+1}/{total_sections})...")
144
+
145
+ # Lấy danh sách các URL bài viết
146
+ article_urls = get_articles_from_section(section_url, num_articles)
147
+
148
+ if article_urls:
149
+ section_progress = st.progress(0)
150
+
151
+ # Crawl chi tiết từng bài viết
152
+ for j, url in enumerate(article_urls):
153
+ article = get_article_details(url)
154
+ if article:
155
+ article_data.append(article)
156
+
157
+ # Cập nhật thanh tiến trình của chuyên mục
158
+ section_progress.progress((j + 1) / len(article_urls))
159
+
160
+ # Cập nhật thanh tiến trình chính
161
+ main_progress.progress((i + 1) / total_sections)
162
+ else:
163
+ # Crawl chuyên mục đã chọn
164
+ with st.spinner(f"Đang crawl dữ liệu từ chuyên mục {selected_section}..."):
165
+ # Lấy danh sách các URL bài viết
166
+ section_url = sections[selected_section]
167
+ article_urls = get_articles_from_section(section_url, num_articles)
168
+
169
+ if article_urls:
170
+ progress_bar = st.progress(0)
171
+ st.write(f"Đã tìm thấy {len(article_urls)} bài viết. Đang crawl chi tiết...")
172
+
173
+ # Crawl chi tiết từng bài viết
174
+ for i, url in enumerate(article_urls):
175
+ article = get_article_details(url)
176
+ if article:
177
+ article_data.append(article)
178
+
179
+ # Cập nhật thanh tiến trình
180
+ progress_bar.progress((i + 1) / len(article_urls))
181
+ else:
182
+ st.error("Không tìm thấy bài viết nào trong chuyên mục này.")
183
+
184
+ # Lưu dữ liệu vào file CSV nếu có dữ liệu
185
+ if article_data:
186
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
187
+ filename = f"vnexpress_{selected_section.replace(' ', '_')}_{timestamp}.csv"
188
+
189
+ if save_to_csv(article_data, filename):
190
+ st.success(f"Đã lưu dữ liệu vào file {filename}")
191
+
192
+ # Hiển thị dữ liệu đã crawl
193
+ st.subheader("Dữ liệu đã crawl:")
194
+ df = pd.DataFrame(article_data)
195
+ st.dataframe(df)
196
+
197
+ # Tạo link tải xuống file CSV
198
+ with open(filename, 'rb') as f:
199
+ csv_data = f.read()
200
+ st.download_button(
201
+ label="Tải xuống file CSV",
202
+ data=csv_data,
203
+ file_name=filename,
204
+ mime="text/csv"
205
+ )
206
+
207
+ # Hiển thị thông tin thống kê
208
+ if selected_section == "Tất cả":
209
+ st.subheader("Thống kê theo chuyên mục:")
210
+ category_counts = df['category'].value_counts().reset_index()
211
+ category_counts.columns = ['Chuyên mục', 'Số lượng bài viết']
212
+ st.dataframe(category_counts)
213
+ else:
214
+ st.error("Không thể crawl dữ liệu hoặc không tìm thấy bài viết nào.")
215
+
216
+ with tab2:
217
+ st.header("Crawl theo URL cụ thể")
218
+
219
+ urls_input = st.text_area("Nhập danh sách URL (mỗi URL một dòng):",
220
+ placeholder="https://vnexpress.net/article1\nhttps://vnexpress.net/article2")
221
+
222
+ if st.button("Bắt đầu crawl theo URL"):
223
+ urls = [url.strip() for url in urls_input.split("\n") if url.strip()]
224
+
225
+ if not urls:
226
+ st.error("Vui lòng nhập ít nhất một URL")
227
+ else:
228
+ with st.spinner(f"Đang crawl {len(urls)} bài viết..."):
229
+ progress_bar = st.progress(0)
230
+ article_data = []
231
+
232
+ for i, url in enumerate(urls):
233
+ article = get_article_details(url)
234
+ if article:
235
+ article_data.append(article)
236
+
237
+ # Cập nhật thanh tiến trình
238
+ progress_bar.progress((i + 1) / len(urls))
239
+
240
+ # Lưu dữ liệu vào file CSV
241
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
242
+ filename = f"vnexpress_custom_{timestamp}.csv"
243
+
244
+ if article_data and save_to_csv(article_data, filename):
245
+ st.success(f"Đã lưu dữ liệu vào file {filename}")
246
+
247
+ # Hiển thị dữ liệu đã crawl
248
+ st.subheader("Dữ liệu đã crawl:")
249
+ df = pd.DataFrame(article_data)
250
+ st.dataframe(df)
251
+
252
+ # Tạo link tải xuống file CSV
253
+ with open(filename, 'rb') as f:
254
+ csv_data = f.read()
255
+ st.download_button(
256
+ label="Tải xuống file CSV",
257
+ data=csv_data,
258
+ file_name=filename,
259
+ mime="text/csv"
260
+ )
261
+ else:
262
+ st.error("Không thể crawl dữ liệu từ các URL đã cung cấp.")
263
+
264
+ # Thêm phần hướng dẫn sử dụng
265
+ with st.expander("Hướng dẫn sử dụng"):
266
+ st.markdown("""
267
+ ### Cách sử dụng:
268
+
269
+ 1. **Crawl theo chuyên mục**:
270
+ - Chọn chuyên mục từ danh sách (hoặc chọn "Tất cả" để crawl tất cả chuyên mục)
271
+ - Nhập số lượng bài viết cần crawl (nếu chọn "Tất cả", đây là số bài viết cho mỗi chuyên mục)
272
+ - Nhấn "Bắt đầu crawl theo chuyên mục"
273
+
274
+ 2. **Crawl theo URL cụ thể**:
275
+ - Nhập danh sách URL (mỗi URL một dòng)
276
+ - Nhấn "Bắt đầu crawl theo URL"
277
+
278
+ 3. **Tải xuống dữ liệu**:
279
+ - Sau khi crawl hoàn tất, nhấn nút "Tải xuống file CSV"
280
+
281
+ ### Lưu ý:
282
+ - File CSV được lưu với encoding UTF-8-sig để hỗ trợ tiếng Việt
283
+ - Tên file bao gồm timestamp để tránh trùng lặp
284
+ - Khi chọn "Tất cả" chuyên mục, quá trình crawl có thể mất nhiều thời gian hơn
285
+ """)
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ beautifulsoup4
2
+ requests
3
+ streamlit