Bạn đã bao giờ đọc lá số tử vi của mình trên báo hay xem trên truyền hình chưa? Chà, tôi không chắc về các quốc gia khác, nhưng ở đất nước Ấn Độ của tôi, mọi người vẫn đọc lá số tử vi của họ.
Và đây là nơi tôi có ý tưởng cho hướng dẫn này. Nghe có vẻ hơi lỗi thời, nhưng trọng tâm chính ở đây không phải là bản thân lá số tử vi – nó chỉ là phương tiện để chúng ta học hỏi.
Trong bài viết này, chúng ta sẽ tìm kiếm một trang web có tên là Horoscope.com bằng cách sử dụng Beautiful Soup và sau đó tạo API của riêng chúng ta bằng Flask. API này, nếu được triển khai trên máy chủ công cộng, sau đó có thể được sử dụng bởi các nhà phát triển khác, những người muốn tạo một trang web để hiển thị tử vi của họ hoặc một ứng dụng tương tự.
Cách thiết lập dự án
Trước hết, chúng ta sẽ tạo một môi trường ảo trong đó chúng ta sẽ cài đặt tất cả các phụ thuộc cần thiết.
Python hiện đã được cài đặt sẵn venv
thư viện. Vì vậy, để tạo một môi trường ảo, bạn có thể sử dụng lệnh dưới đây:
$ python -m venv env
Để kích hoạt môi trường ảo có tên env
sử dụng lệnh:
env\Scripts\activate.bat
source env/bin/activate
Để hủy kích hoạt môi trường (không bắt buộc ở giai đoạn này):
deactivate
Bây giờ chúng tôi đã sẵn sàng để cài đặt các phụ thuộc. Các mô-đun và thư viện chúng tôi sẽ sử dụng trong dự án này là:
Chúng tôi cũng sẽ sử dụng các biến môi trường trong dự án này. Vì vậy, chúng ta sẽ cài đặt một mô-đun khác có tên python-tách rời để xử lý việc này:
pip install python-decouple
Để tìm hiểu thêm về các biến môi trường trong Python, bạn có thể xem bài viết này.
Quy trình công việc dự án
Quy trình làm việc cơ bản của dự án sẽ như thế này:
- Dữ liệu tử vi sẽ được lấy từ Horoscope.com.
- Sau đó, dữ liệu sẽ được máy chủ Flask của chúng tôi sử dụng để gửi phản hồi JSON cho người dùng.
Cách thiết lập dự án Flask
Điều đầu tiên chúng ta sẽ làm là tạo một dự án Flask. Nếu bạn kiểm tra tài liệu chính thức của Flask, bạn sẽ tìm thấy một ứng dụng tối thiểu ở đó.
Nhưng, chúng tôi sẽ không làm theo điều đó. Chúng tôi sẽ viết một ứng dụng có thể mở rộng hơn và có cấu trúc cơ sở tốt. Nếu muốn, bạn có thể làm theo hướng dẫn này để bắt đầu với Flask.
Ứng dụng của chúng tôi sẽ tồn tại trong một gói có tên cốt lõi. Để chuyển đổi một thư mục thông thường thành một gói Python, chúng ta chỉ cần thêm một __init__.py
tập tin. Vì vậy, trước tiên hãy tạo gói cốt lõi của chúng tôi.
$ mkdir core
Sau đó, hãy tạo __init__.py
tập tin bên trong thư mục lõi:
$ cd core
$ touch __init__.py
$ cd ..
Trong thư mục gốc của dự án, tạo một tệp có tên config.py
. Chúng tôi sẽ lưu trữ các cấu hình cho dự án trong tệp này. Trong tệp, thêm nội dung sau:
from decouple import config
class Config(object):
SECRET_KEY = config('SECRET_KEY', default="guess-me")
DEBUG = False
TESTING = False
CSRF_ENABLED = True
class ProductionConfig(Config):
DEBUG = False
MAIL_DEBUG = False
class StagingConfig(Config):
DEVELOPMENT = True
DEBUG = True
class DevelopmentConfig(Config):
DEVELOPMENT = True
DEBUG = True
class TestingConfig(Config):
TESTING = True
Trong kịch bản trên, chúng tôi đã tạo một cấu hình class và định nghĩa các thuộc tính khác nhau bên trong đó. Ngoài ra, chúng tôi đã tạo các lớp con khác nhau (theo các giai đoạn phát triển khác nhau) kế thừa cấu hình lớp.
Lưu ý rằng chúng tôi đã đặt SECRET_KEY thành một biến môi trường có tên CHÌA KHOÁ BÍ MẬT. Tạo một tệp có tên .env
trong thư mục gốc và thêm nội dung sau vào đó:
APP_SETTINGS=config.DevelopmentConfig
SECRET_KEY=gufldksfjsdf
Ngoại trừ CHÌA KHOÁ BÍ MẬT, chúng ta có APP_SETTINGS đề cập đến một trong những lớp chúng tôi đã tạo trong config.py
tập tin. Chúng tôi đặt nó vào giai đoạn hiện tại của dự án.
Bây giờ, chúng ta có thể thêm nội dung sau vào __init__.py
tập tin:
from flask import Flask
from decouple import config
from flask_restx import Api
app = Flask(__name__)
app.config.from_object(config("APP_SETTINGS"))
api = Api(
app,
version='1.0',
title="Horoscope API",
description='Get horoscope data easily using the below APIs',
license="MIT",
contact="Ashutosh Krishna",
contact_url="https://ashutoshkrris.tk",
contact_email="[email protected]",
doc="/",
prefix='/api/v1'
)
Trong tập lệnh Python ở trên, trước tiên chúng tôi nhập lớp Flask từ mô-đun Flask mà chúng tôi đã cài đặt. Tiếp theo, chúng ta đang tạo một đối tượng app
của lớp Flask. chúng tôi sử dụng __name__
đối số để chỉ ra mô-đun hoặc gói của ứng dụng, để Flask biết nơi tìm các tệp khác, chẳng hạn như mẫu.
Tiếp theo, chúng tôi đang đặt cấu hình ứng dụng thành APP_SETTINGS theo biến trong .env
tập tin.
Ngoài ra, chúng tôi đã tạo một đối tượng của api lớp. Chúng ta cần truyền các đối số khác nhau cho nó. Chúng ta có thể tìm thấy tài liệu về Swagger trên /
tuyến đường. Các /api/v1
sẽ được thêm tiền tố trên mỗi tuyến API.
Bây giờ, hãy tạo một routes.py
tập tin trong core
gói và chỉ cần thêm không gian tên sau:
from core import api
from flask import jsonify
ns = api.namespace('/', description='Horoscope APIs')
Chúng ta cần nhập các tuyến đường trong __init__.py
tập tin:
from flask import Flask
from decouple import config
from flask_restx import Api
app = Flask(__name__)
app.config.from_object(config("APP_SETTINGS"))
api = Api(
app,
version='1.0',
title="Horoscope API",
description='Get horoscope data easily using the below APIs',
license="MIT",
contact="Ashutosh Krishna",
contact_url="https://ashutoshkrris.tk",
contact_email="[email protected]",
doc="/",
prefix='/api/v1'
)
from core import routes # Add this line
Bây giờ chúng tôi chỉ còn lại một tệp sẽ giúp chúng tôi chạy máy chủ Flask:
from core import app
if __name__ == '__main__':
app.run()
Khi bạn chạy tệp này bằng cách sử dụng python main.py
lệnh, bạn sẽ thấy một kết quả tương tự:

Bây giờ, chúng tôi đã sẵn sàng thu thập dữ liệu từ trang web Tử vi.
Cách cạo dữ liệu từ Horoscope.com
Nếu bạn mở Tử vi.com và chọn cung hoàng đạo của mình, dữ liệu tử vi cho cung hoàng đạo của bạn hôm nay sẽ được hiển thị.

Trong hình trên, bạn có thể thấy bạn có thể xem tử vi cho ngày hôm qua, ngày mai, hàng tuần, hàng tháng hoặc thậm chí là một ngày tùy chỉnh. Chúng tôi sẽ sử dụng tất cả những thứ này.
Nhưng trước tiên, nếu bạn thấy URL của trang hiện tại, URL đó sẽ giống như: https://www.horcop.com/us/horoscopes/General/horoscope-General-daily-today.aspx?sign=10 .
URL có hai biến, nếu bạn thấy rõ, dấu hiệu và hôm nay. Giá trị của biến dấu hiệu sẽ được phân theo cung hoàng đạo. biến hôm nay có thể được thay thế bằng hôm qua và ngày mai.
Từ điển dưới đây có thể giúp chúng ta về các cung hoàng đạo:
ZODIAC_SIGNS = {
"Aries": 1,
"Taurus": 2,
"Gemini": 3,
"Cancer": 4,
"Leo": 5,
"Virgo": 6,
"Libra": 7,
"Scorpio": 8,
"Sagittarius": 9,
"Capricorn": 10,
"Aquarius": 11,
"Pisces": 12
}
Điều này có nghĩa là nếu cung hoàng đạo của bạn là Ma Kếtgiá trị của dấu hiệu trong URL sẽ là 10.
Tiếp theo, nếu chúng tôi muốn lấy dữ liệu tử vi cho một ngày tùy chỉnh, URL https://www.horcop.com/us/horoscopes/General/horcop-archive.aspx?sign=10&laDate=20211213 sẽ giúp chúng tôi.
Nó có cùng dấu hiệu biến, nhưng nó có một biến khác laDate trong đó có ngày trong YYYYMMDD định dạng.
Bây giờ, chúng ta đã sẵn sàng để tạo các chức năng khác nhau để lấy dữ liệu tử vi. Tạo một utils.py
tập tin và làm theo cùng.
Howe để có được một Tử vi trong ngày
import requests
from bs4 import BeautifulSoup
def get_horoscope_by_day(zodiac_sign: int, day: str):
if not "-" in day:
res = requests.get(f"https://www.horoscope.com/us/horoscopes/general/horoscope-general-daily-{day}.aspx?sign={zodiac_sign}")
else:
day = day.replace("-", "")
res = requests.get(f"https://www.horoscope.com/us/horoscopes/general/horoscope-archive.aspx?sign={zodiac_sign}&laDate={day}")
soup = BeautifulSoup(res.content, 'html.parser')
data = soup.find('div', attrs={'class': 'main-horoscope'})
return data.p.text
Chúng tôi đã tạo hàm đầu tiên chấp nhận hai đối số – một số nguyên biểu tượng hoàng đạo và một chuỗi ngày. Ngày có thể là hôm nay, ngày mai, hôm qua hoặc bất kỳ ngày tùy chỉnh nào trước ngày hôm nay ở định dạng YYYY-MM-DD.
Nếu ngày không phải là ngày tùy chỉnh, ngày đó sẽ không có ký hiệu dấu gạch nối (-) trong đó. Vì vậy, chúng tôi đã đặt một điều kiện cho cùng.
Nếu không có ký hiệu gạch nối, chúng tôi thực hiện yêu cầu GET trên https://www.horoscope.com/us/horoscopes/general/horoscope-general-daily-{day}.aspx?sign={zodiac_sign}
. Trước tiên, chúng tôi thay đổi ngày từ định dạng YYYY-MM-DD thành YYYYMMDD.
Sau đó, chúng tôi thực hiện một yêu cầu GET trên https://www.horoscope.com/us/horoscopes/general/horoscope-archive.aspx?sign={zodiac_sign}&laDate={day}
.
Sau đó, chúng tôi lấy dữ liệu HTML từ nội dung phản hồi của trang bằng BeautifulSoup. Bây giờ chúng ta cần lấy văn bản tử vi từ mã HTML này. Nếu bạn kiểm tra mã của bất kỳ trang web nào, bạn sẽ thấy điều này:

Các văn bản tử vi được chứa trong một div với lớp chính-tử vi. Vì vậy, chúng tôi sử dụng các soup.find()
chức năng trích xuất chuỗi văn bản đoạn văn và trả lại.
Cách Nhận Tử Vi Trong Tuần
def get_horoscope_by_week(zodiac_sign: int):
res = requests.get(f"https://www.horoscope.com/us/horoscopes/general/horoscope-general-weekly.aspx?sign={zodiac_sign}")
soup = BeautifulSoup(res.content, 'html.parser')
data = soup.find('div', attrs={'class': 'main-horoscope'})
return data.p.text
Chức năng trên khá giống với chức năng trước. Chúng tôi vừa thay đổi URL thành https://www.horoscope.com/us/horoscopes/general/horoscope-general-weekly.aspx?sign={zodiac_sign}
.
Cách lấy Tử vi trong tháng
def get_horoscope_by_month(zodiac_sign: int):
res = requests.get(f"https://www.horoscope.com/us/horoscopes/general/horoscope-general-monthly.aspx?sign={zodiac_sign}")
soup = BeautifulSoup(res.content, 'html.parser')
data = soup.find('div', attrs={'class': 'main-horoscope'})
return data.p.text
Chức năng này cũng tương tự như hai chức năng còn lại ngoại trừ URL hiện đã được thay đổi thành https://www.horoscope.com/us/horoscopes/general/horoscope-general-monthly.aspx?sign={zodiac_sign}
.
Cách tạo các tuyến API
Chúng tôi sẽ sử dụng Flask-RESTX để tạo các tuyến API của mình. Các tuyến API sẽ giống như sau:
- Đối với ngày hàng ngày hoặc tùy chỉnh:
/api/v1/get-horoscope/daily?day=today&sign=capricorn
hoặcapi/v1/get-horoscope/daily?day=2022-12-14&sign=capricorn
- Đối với hàng tuần:
api/v1/get-horoscope/weekly?sign=capricorn
- Đối với hàng tháng:
api/v1/get-horoscope/monthly?sign=capricorn
Chúng tôi có hai tham số truy vấn trong các URL: ngày và dấu hiệu. Các ngày tham số có thể nhận các giá trị như hôm nay, hôm qua hoặc ngày tùy chỉnh như 2022-12-14. Các dấu hiệu tham số sẽ lấy tên cung hoàng đạo có thể viết hoa hoặc viết thường, không thành vấn đề.
Để phân tích cú pháp các tham số truy vấn từ URL, Flask-RESTX có hỗ trợ tích hợp để xác thực dữ liệu yêu cầu bằng thư viện tương tự như argparse gọi điện reqparse. Để thêm các đối số trong URL, chúng tôi sẽ sử dụng add_argument phương pháp của RequestParser lớp.
parser = reqparse.RequestParser()
parser.add_argument('sign', type=str, required=True)
Các type
tham số sẽ lấy loại tham số. Các required=True
làm cho tham số truy vấn bắt buộc phải được thông qua.
Bây giờ, chúng ta cần một tham số truy vấn khác. Nhưng tham số này sẽ chỉ được sử dụng trong URL tử vi hàng ngày.
Thay vì viết lại các đối số, chúng ta có thể viết một trình phân tích cú pháp gốc chứa tất cả các đối số được chia sẻ và sau đó mở rộng trình phân tích cú pháp bằng copy()
.
parser_copy = parser.copy()
parser_copy.add_argument('day', type=str, required=True)
Các parser_copy
sẽ không chỉ chứa ngàynhưng cũng dấu hiệu. Đó là những gì chúng tôi sẽ yêu cầu cho tử vi hàng ngày.
Các khối xây dựng chính do Flask-RESTX cung cấp là tài nguyên. Các tài nguyên được xây dựng dựa trên các chế độ xem có thể cắm Flask, giúp bạn dễ dàng truy cập vào nhiều phương thức HTTP chỉ bằng cách xác định các phương thức trên tài nguyên của bạn.
Hãy tạo ra API tử vi hàng ngày lớp kế thừa Nguồn lớp từ flask_restx
.
@ns.route('/get-horoscope/daily')
class DailyHoroscopeAPI(Resource):
'''Shows daily horoscope of zodiac signs'''
@ns.doc(parser=parser_copy)
def get(self):
args = parser_copy.parse_args()
day = args.get('day')
zodiac_sign = args.get('sign')
try:
zodiac_num = ZODIAC_SIGNS[zodiac_sign.capitalize()]
if "-" in day:
datetime.strptime(day, '%Y-%m-%d')
horoscope_data = get_horoscope_by_day(zodiac_num, day)
return jsonify(success=True, data=horoscope_data, status=200)
except KeyError:
raise NotFound('No such zodiac sign exists')
except AttributeError:
raise BadRequest(
'Something went wrong, please check the URL and the arguments.')
except ValueError:
raise BadRequest('Please enter day in correct format: YYYY-MM-DD')
Các @ns.route()
trình trang trí đặt tuyến API. Bên trong API tử vi hàng ngày lớp, chúng ta có được phương thức sẽ xử lý các yêu cầu GET. Các @ns.doc()
decorator sẽ giúp chúng ta thêm các tham số truy vấn vào URL.
Để có được các giá trị của tham số truy vấn, chúng tôi sẽ sử dụng parse_args() phương thức sẽ trả về cho chúng ta một từ điển như thế này:
{'sign': 'capricorn', 'day': '2022-12-14'}
Sau đó chúng ta có thể lấy các giá trị bằng cách sử dụng các phím ngày và dấu hiệu.
Như đã định nghĩa ở phần đầu, chúng ta sẽ có một từ điển ZODIAC_SIGNS. chúng tôi sử dụng một thử ngoại trừ khối để xử lý yêu cầu. Nếu cung hoàng đạo không có trong từ điển, một Lỗi chính Ngoại lệ được nâng lên. Trong trường hợp đó, chúng tôi trả lời với một Không tìm thấy lỗi (Lỗi 404).
Ngoài ra, nếu ngày có dấu gạch nối trong đó, chúng tôi sẽ cố gắng đối sánh định dạng ngày với YYYY-MM-DD. Nếu nó không ở định dạng đó, chúng tôi sẽ tăng Yêu cầu xấu lỗi (Lỗi 400). Nếu ngày không chứa dấu gạch nối, chúng tôi gọi trực tiếp get_horoscope_by_day()
phương pháp với dấu hiệu và ngày tranh luận.
Nếu một số từ ngữ vô nghĩa được chuyển thành giá trị tham số, một Lỗi thuộc tính được nuôi dưỡng. Trong trường hợp đó, chúng tôi nâng cao một Yêu cầu xấu lỗi.
Hai con đường còn lại cũng khá giống con đường trên. Sự khác biệt là chúng ta không cần tham số ngày ở đây. Vì vậy, thay vì sử dụng parser_copy
chúng tôi sẽ sử dụng parser
đây.
@ns.route('/get-horoscope/weekly')
class WeeklyHoroscopeAPI(Resource):
'''Shows weekly horoscope of zodiac signs'''
@ns.doc(parser=parser)
def get(self):
args = parser.parse_args()
zodiac_sign = args.get('sign')
try:
zodiac_num = ZODIAC_SIGNS[zodiac_sign.capitalize()]
horoscope_data = get_horoscope_by_week(zodiac_num)
return jsonify(success=True, data=horoscope_data, status=200)
except KeyError:
raise NotFound('No such zodiac sign exists')
except AttributeError:
raise BadRequest('Something went wrong, please check the URL and the arguments.')
@ns.route('/get-horoscope/monthly')
class MonthlyHoroscopeAPI(Resource):
'''Shows monthly horoscope of zodiac signs'''
@ns.doc(parser=parser)
def get(self):
args = parser.parse_args()
zodiac_sign = args.get('sign')
try:
zodiac_num = ZODIAC_SIGNS[zodiac_sign.capitalize()]
horoscope_data = get_horoscope_by_month(zodiac_num)
return jsonify(success=True, data=horoscope_data, status=200)
except KeyError:
raise NotFound('No such zodiac sign exists')
except AttributeError:
raise BadRequest('Something went wrong, please check the URL and the arguments.')
Bây giờ các tuyến đường của chúng tôi đã được thực hiện. Để kiểm tra API, bạn có thể sử dụng tài liệu Swagger có sẵn trên /
route hoặc bạn có thể sử dụng Postman. Hãy chạy máy chủ và kiểm tra nó.
Bạn cũng có thể triển khai dự án trên máy chủ công cộng để các nhà phát triển khác cũng có thể truy cập và sử dụng API.
kết thúc
Trong hướng dẫn này, chúng ta đã học cách cạo dữ liệu từ một trang web bằng cách sử dụng các yêu cầu và Beautiful Soup. Sau đó, chúng tôi đã tạo một API bằng Flask và Flask-RESTX.
Nếu bạn muốn tìm hiểu cách tương tác với API bằng Python, hãy xem hướng dẫn này.
Tôi hy vọng bạn thích nó – và cảm ơn vì đã đọc!
Mã cho hướng dẫn: https://github.com/ashutoshkrris/Horoscope-API