Cùng Python Crawl Dữ Liệu Để Tạo Bài Báo Nhanh
Hôm nay mình sẽ cùng các bạn tìm hiểu về một Module khá phổ biến của Python đó là Request
và cùng sử dụng nó để làm một tool crawl dữ liệu nhé. Đầu tiên xem thử sau bài này chúng ta sẽ làm được gì nào.
Bạn đang đọc: Cùng Python Crawl Dữ Liệu Để Tạo Bài Báo Nhanh
Sau bài viết này tất cả chúng ta sẽ crawl được tổng thể những tin tức mới từ một trang báo điện tử và sử dụng chúng để tạo ra những mẫu tin tức nhanh như trên ảnh với 4 bước chính :
- Cài đặt module
- Lấy danh sách tin tức mới nhất
- Lấy dữ liệu tin tức chi tiết từng bài
- Tạo những mẫu tin tức nhanh từ dữ liệu ở trên
Cùng mở màn thôi ! !
1. Cài đặt Module (Hướng dẫn trên cmd Window)
- Cài đặt Requests:
pip install requests (hoặc python –m pip install requests)
- Cài đặt Pilow:
pip install Pillow (hoặc python –m pip install Pillow)
* Note : Nếu xài PIP cũ thì mọi người update lên pip mới trước khi cài Pillow nhé :
- Update PIP:
pip install -–upgrade pip (hoặc python –m pip install -–upgrade pip)
Trong quy trình thiết lập nếu có lỗi gì thì mọi người post lên để cùng tìm cách fix nhé !
2. Cào dữ liệu danh sách tin tức mới
2.1. Lấy dữ liệu
Hiểu nôm na thì module Request dùng để gửi HTTP request, giống như thao tác bạn thường làm khi lướt mạng : Vào trình duyệt gõ codelearn.io và enter, bạn sẽ nhận được giao diện của trang web hoặc một dạng dữ liệu khác. Để lấy được dữ liệu trả về thì ta phải sử dụng một module hỗ trợ và
Request
sẽ giúp chúng ta làm điều đó. Cùng nhau tìm hiểu các dùng nhé !requests.method(url, params, data, json, headers, cookies, files, auth, timeout, allow_redirects, proxies, verify, stream, cert)
What ? ? Cần nhiều tham số thế cơ á ? Không, tất cả chúng ta chỉ cần lấy dữ liệu từ một trang tin tức thôi, không cần gửi đi dữ liệu gì cả. Hãy thử thế này nhé .
import requests response = requests.get("https://tuoitre.vn/tin-moi-nhat.htm") print(response)
Và đây là tác dụng :
Thật là No hope, chúng ra đang cần một trang web cơ mà. Thử gọi vài thuộc tính ra thử xem nào :
print(response.content)
Kết quả :
\r\n \r\n …(còn nữa>…
Chúng ra đã lấy được dữ liệu của một trang web, vấn đề bây giờ là tách dữ liệu.
2.2. Tách dữ liệu
Có nhiều cách để bóc tách dữ liệu từ một văn bản dài, sử dụng
regex
(biểu thức chính quy) cũng là một cách nhưng thực tế thì python đã hỗ trợ mạnh hơn. Cùng tìm hiểu về modulebeautifulSoup4
nhé.Cách thiết lập :
pip install beautifulsoup4 (hoặc python –m pip install beautifulsoup4
Beautiful Soup sẽ giúp tất cả chúng ta nghiên cứu và phân tích dữ liệu HTML hay XML thành dữ liệu cây, từ đó giúp tất cả chúng ta truy xuất dữ liệu thuận tiện hơn. Cùng test thử nhé
import requests from bs4 import BeautifulSoup response = requests.get("https://tuoitre.vn/tin-moi-nhat.htm") soup = BeautifulSoup(response.content, "html.parser") print(soup)
Thành quả là bạn sẽ được in ra một trang html rất ngăn nắp như thế này :
2.3. Phân tích dữ liệu
Bước tiếp theo là chúng ta cần phân tích xem dữ liệu cần lấy ở đâu. Rõ ràng để lấy dữ liệu chi tiết một bài báo ta cần liên kết đến bài đó nhỉ. Bật f12 lên và phân tích chút:
Sau thời hạn mò mẫm, tất cả chúng ta hoàn toàn có thể tìm thấy link bài báo ở trong thẻ và thẻ này nằm trong thẻ h3 có class “ title-news ”. Vậy việc làm của chúng ra là lọc toàn bộ thẻ h3 có class “ title-news ” và lấy thẻ a trong nó, cùng code tiếp nào :
titles = soup.findAll('h3', class_='title-news') print(titles)
Sau khi thêm đoạn này tất cả chúng ta sẽ được một mảng những thẻ h3 là tiêu đề bài báo :
Tiếp tục việc làm tiếp theo là lấy link của tổng thể những bài viết đó :
links = [link.find('a').attrs["href"] for link in titles] print(links)
Kết quả :
['/moi-cac-truong-tham-gia-cam-nang-tuyen-sinh-dh-cd-hau-covid-19-tang-thi-sinh-20200605093106804.htm', '/truong-tre-vao-top-10-cong-bo-quoc-te-va-nghi-van-mua-bai-bao-khoa-hoc-2020060509193892.htm', '/nghi-si-9-nuoc-lap-lien-minh-dua-ra-lap-truong-cung-ran-hon-voi-trung-quoc-20200605084031973.htm', '/mo-tinh-xuat-huyet-nao-20200605092647937.htm',…]
Một mảng những link, khá hợp lý. Giải thích một chút ít nhé : Ở trên để tìm tổng thể những thẻ h3 nên ta sử dụng hàm findAll ( ) thõa mãn class truyền vào, nhưng ở dưới chỉ cần tìm duy nhất 1 thẻ a trong h3 nên ta chỉ cần dùng lệnh find ( “ a ” ) và dùng attrs [ “ thuoc_tinh ” ] để lấy thuộc tính của thẻ a .
3. Lấy dữ liệu chi tiết từng bài
OK coi như tất cả chúng ta đã lấy được tổng thể bài viết. Công việc tiếp theo là truy vấn từng bài viết, lấy một ảnh làm đại diện thay mặt và một đoạn trích ngắn. Do phần này tựa như trên nên mình lướt nhanh một chút ít :
for link in links: news = requests.get("https://tuoitre.vn" + link) soup = BeautifulSoup(news.content, "html.parser") title = soup.find("h1", class_="article-title").text abstract = soup.find("h2", class_="sapo").text body = soup.find("div", id="main-detail-body") content = body.findChildren("p", recursive=False)[0].text + body.findChildren("p", recursive=False)[1].text image = body.find("img").attrs["src"] print("Tiêu đề: " + title) print("Mô tả: " + abstract) print("Nội dung: " + content) print("Ảnh minh họa: " + image) print("_________________________________________________________________________")
Vậy là xong phần crawl dữ liệu rồi. Cũng đơn thuần phải không ? Mình sẽ lý giải chút xíu
Đầu tiên chúng ta dùng một vòng
for-loop
để duyệt qua tất cả các link và truy cập các link đó, các bạn chú ý do href của thẻ a sẽ không có link gốc (dạng “/router-ne”) nên chúng ta cần chèn thêm BASE URL vào nhé :requests.get("https://tuoitre.vn" + link)
Ở bước lấy title, tóm tắt và ảnh. Bạn bật f12 lên tìm hiểu và khám phá một tí là ra. Còn phần content mình cần tìm 2 thẻ p con chỉ dưới
một cấp nên ta sẽ có tham số recursive như sau :
body.findChildren("p", recursive=False)
Thành quả
Kết quả đã có, bây giờ chúng ta sẽ xây dựng một hàm
crawNewsData()
để tiện cho việc tái sử dụng nhé, hàm này sẽ nhận vào url gốc, url đến nơi lấy bài và trả về một list các bài viết gồm tiêu đề, tóm tắt, nội dung và một bức ảnh đại diện.def crawNewsData(baseUrl, url): response = requests.get(url) soup = BeautifulSoup(response.content, "html.parser") titles = soup.findAll('h3', class_='title-news') links = [link.find('a').attrs["href"] for link in titles] data = [] for link in links: news = requests.get(baseUrl + link) soup = BeautifulSoup(news.content, "html.parser") title = soup.find("h1", class_="article-title").text abstract = soup.find("h2", class_="sapo").text body = soup.find("div", id="main-detail-body") content = "" try: content = body.findChildren("p", recursive=False)[0].text + body.findChildren("p", recursive=False)[1].text except: content = "" image = body.find("img").attrs["src"] data.append({ "title": title, "abstract": abstract, "content": content, "image": image, }) return data
4. Trình bày tin tức trên một bức ảnh
Để trình bày nội dung lên một bức ảnh, chúng ta sẽ dử dụng module
Pillow
. Pillow là module hỗ trợ xử lí file ảnh khá thân thiện và dễ sử dụng. Bắt tay vào làm luôn nhé:Test thử vài tính năng mà tất cả chúng ta sẽ sử dụng nào :
from PIL import Image, ImageDraw, ImageFont img = Image.new('RGB', (650, 625), color="white") font = ImageFont.load_default() d = ImageDraw.Draw(img) d.text((10, 10), "Hello World", font=font, fill="black") img.show()
Ok vậy là chúng ra đã tạo được một bức ảnh và viết chữ lên nó. Tiếp theo là chèn một bức ảnh vào bức ảnh khác và viết chữ Tiếng Việt lên :
img = Image.new('RGB', (650, 625), color="white") font = ImageFont.truetype("font/arial.ttf", 12) d = ImageDraw.Draw(img) addImg = Image.open("anh.jpg") img.paste(addImg, (10, 10)) d.text((300, 10), "Cái text nè ahihi", font=font, fill="black") img.show()
Chúng ta sử dụng
Image.open()
để mở thêm một ảnh và phương thức paste để chèn một ảnh vào ảnh khác với tham số truyền vào là ảnh được open ở trên và tuple chỉ tọa độ sẽ chèn.Lưu ý: là để draw được Tiếng Việt thì bạn cần phải load font có hỗ trợ Tiếng Việt vào như đoạn trên, cái path tới font ấy bạn có thể dẫn tới font đang lưu trong hệ thống hoặc tạo một folder font cùng cấp với file code để lưu font như mình nhé.
Bước ở đầu cuối :
Mình sẽ tạo bố cục tổng quan bài báo như thế này, mọi người hoàn toàn có thể tùy sức phát minh sáng tạo theo ý mình nhé, code thôi nào .
Đầu tiên những bạn sẽ thấy việc làm chèn chữ lên ảnh sẽ được lặp đi lặp lại nên mình sẽ viết riêng một hàm để chèn chữ nhé :def writeToImage(image, text, position, font, color, maxLine): charPerLine = 650 // font.getsize('x')[0] pen = ImageDraw.Draw(image) yStart = position[1] xStart = position[0] point = 0 prePoint = 0 while point < len(text): prePoint = point point += charPerLine while point < len(text) and text[point] != " ": point -= 1 pen.text((xStart, yStart), text[prePoint:point], font=font, fill=color) yStart += font.getsize('hg')[1] maxLine -= 1 if (maxLine == 0): if (point < len(text)): pen.text((xStart, yStart), "...", font=font, fill="black") break
Ơ … sao phức tạp thế nhỉ ? ? Hàm để ghi chữ lúc nãy ngắn lắm cơ mà. Thực ra tất cả chúng ta còn một yếu tố khi chèn chữ đó là khi bạn ghi một chuỗi dài, nó không biết tự động hóa xuống dòng khi vượt ra ngoài bức ảnh. Chính vì thế tất cả chúng ta sẽ xử lí theo hướng sau :
- Biến charPerLine sẽ lưu số kí tự có thể chứa trên 1 dòng bằng cách lấy độ dài trang chia cho độ dài 1 kí tự, từ đó ta sẽ cắt chuỗi với mỗi chuỗi có
charPerLine
kí tự hoặc bé hơn- 2 biến
prePoint
và point sẽ lưu điểm đầu và điểm cuối của dòng được cắt- Sau khi in một dòng, chúng ta sẽ cộng tọa độ y thêm 1 đoạn đúng bằng độ cao 1 dòng chữ.
- Trường hợp một nội dung quá dài, ta cũng chỉ in
maxLine
dòng rồi in thêm dấu …Hợp lý hơn rồi đó. Đến phần cuối thôi :
def makeFastNews(data): for index, item in enumerate(data): # make new image and tool to draw image = Image.new('RGB', (650, 750), color="white") pen = ImageDraw.Draw(image) # load image from internet => resize => paste to main image pen.rectangle(((0, 0), (650, 300)), fill="grey") newsImage = Image.open(requests.get(item["image"], stream=True).raw) newsImage.thumbnail((650, 300), Image.ANTIALIAS) image.paste(newsImage, (650 // 2 - newsImage.width // 2, 300 // 2 - newsImage.height//2)) ## write title titleFont = ImageFont.truetype("font/arial.ttf", 22) writeToImage(image, item["title"], (10, 310), titleFont, "black", 3) abstractFont = ImageFont.truetype("font/arial.ttf", 15) writeToImage(image, item["abstract"], (10, 390), abstractFont, "gray", 4) contentFont = ImageFont.truetype("font/arial.ttf", 20) writeToImage(image, item["content"], (10, 460), contentFont, "black", 11) name = "news-" + str(index) + ".png" image.save("news/" + name) print("saved to " + "news/" + name)
Đây là đoạn code ở đầu cuối, mình xin lý giải chút nhé :
- Hàm này sẽ nhận dữ liệu mình vừa crawl về lúc nãy.
- Chúng ta sẽ dùng một vòng for để duyệt hết giá trị trong data, nhưng dùng thêm hàm
enumerate()
để có thể lấy được chỉ số, để lát nữa mình tạo tên file khỏi bị trùng nhé.# load image from internet => resize => paste to main image pen.rectangle(((0, 0), (650, 300)), fill="grey") newsImage = Image.open(requests.get(item["image"], stream=True).raw) newsImage.thumbnail((650, 300), Image.ANTIALIAS) image.paste(newsImage, (650 // 2 - newsImage.width // 2, 300 // 2 - newsImage.height//2))
Đoạn này sẽ xử lí phần chèn ảnh bài báo, chúng ra dùng
pen.rectangle()
để vẽ một hình chữ nhật làm nền, chúng ta sẽ truyền vào tọa độ góc trên, chiều dài, rộng và màu.Tiếp theo vì ảnh trên mạng sẽ có size tùy ý nên tất cả chúng ta sẽ chuyển nó về ảnh thumbnail để thuận tiện chèn vào .
Mình vẫn dùng
request.get()
để lấy dử liệu là một bức ảnh và thuộc tính raw để lấy file raw truyền vào Image.open()Tiếp theo tất cả chúng ta sẽ dùng lệnh này để resize bước ảnh theo ý muốn vẫn giữ nguyên tỉ lệ
newsImage.thumbnail((650, 300), Image.ANTIALIAS)
Và phần còn lại chỉ là gọi hàm:
writeToImage()
để ghi tiêu đề, tóm tắt và nội dùngCuối cùng chúng ra dùng phương thức
Image.save(fileName)
để lưu ảnhBạn hoàn toàn có thể tạo một thư mục để lưu những file ảnh tạo ra như mình và tạo tên ảnh bằng những index lúc nãy để tránh trùng tên :
fileName = "folder_ban_tao/" + "news-" + str(index) + ".png"
Thành quả
Cuối cùng cũng đã xong, và đây là thành quả.
Trong quy trình thực thi nếu có lỗi gì thì mọi người cùng comment lên để luận bàn nhé. Thank for reading <3 FullCode : Link gitHub
Source: https://thomaygiat.com
Category : Kỹ Thuật Số
Chuyển vùng quốc tế MobiFone và 4 điều cần biết – MobifoneGo
Muốn chuyển vùng quốc tế đối với thuê bao MobiFone thì có những cách nào? Đừng lo lắng, bài viết này của MobiFoneGo sẽ giúp…
Cách copy dữ liệu từ ổ cứng này sang ổ cứng khác
Bạn đang vướng mắc không biết làm thế nào để hoàn toàn có thể copy dữ liệu từ ổ cứng này sang ổ cứng khác…
Hướng dẫn xử lý dữ liệu từ máy chấm công bằng Excel
Hướng dẫn xử lý dữ liệu từ máy chấm công bằng Excel Xử lý dữ liệu từ máy chấm công là việc làm vô cùng…
Cách nhanh nhất để chuyển đổi từ Android sang iPhone 11 | https://thomaygiat.com
Bạn đã mua cho mình một chiếc iPhone 11 mới lạ vừa ra mắt, hoặc có thể bạn đã vung tiền và có một chiếc…
Giải pháp bảo mật thông tin trong các hệ cơ sở dữ liệu phổ biến hiện nay
Hiện nay, với sự phát triển mạnh mẽ của công nghệ 4.0 trong đó có internet và các thiết bị công nghệ số. Với các…
4 điều bạn cần lưu ý khi sao lưu dữ liệu trên máy tính
08/10/2020những chú ý khi tiến hành sao lưu dữ liệu trên máy tính trong bài viết dưới đây của máy tính An Phát để bạn…