您是否曾在报纸上读过或看过电视上的星座运势?嗯,我不确定其他国家的情况,但在我的国家印度,人们仍然会阅读星座运势。

这就是我写这个教程的灵感来源。听起来可能有点过时,但这里的主要重点不是星座本身——它只是我们学习的载体。

在本文中,我们将使用 Beautiful Soup 抓取一个名为 Horoscope.com ,然后使用 Flask 创建我们自己的 API。如果将此 API 部署在公共服务器上,其他希望创建网站来展示其星座运势或应用程序的开发人员可以使用此 API。

如何设置项目

首先,我们将创建一个虚拟环境,在其中安装所有必需的依赖项。

Python 现在附带了预安装的 venv 库。因此,要创建虚拟环境,您可以使用以下命令:

$ python -m venv env

要激活名为的虚拟环境 env ,请使用以下命令:

  • 在 Windows 上:
env\Scripts\activate.bat
  • 在 Linux 和 MacOS 上:
source env/bin/activate

要停用环境(此阶段不需要):

deactivate

现在我们准备安装依赖项。我们将在此项目中使用的模块和库是:

  • 请求: 请求 允许您非常轻松地发送 HTTP/1.1 请求。该模块未随 Python 预装,因此我们需要使用以下命令进行安装:
    $ pip install requests
  • bs4: Beautiful Soup (bs4) 是一个用于从 HTML 和 XML 文件中提取数据的 Python 库。该模块未随 Python 预装,因此我们需要使用以下命令进行安装:
    $ pip install bs4
  • Flask: Flask 是一个简单易用的 Python 微框架,可帮助构建可扩展且安全的 Web 应用程序。该模块未随 Python 预装,因此我们需要使用以下命令进行安装:
    $ pip install flask
  • Flask-RESTX: Flask-RESTX 可让您使用 Swagger 文档创建 API。该模块未随 Python 预装,因此我们需要使用以下命令进行安装:
    $ pip install flask-restx

我们还将在此项目中使用环境变量。因此,我们将安装另一个名为 python-decouple 来处理此问题:

pip install python-decouple

要了解有关 Python 中的环境变量的更多信息,可以查看 这篇文章 .

项目工作流程

该项目的基本工作流程如下:

  1. 星座数据将从 Horoscope.com .
  2. 然后我们的 Flask 服务器将使用这些数据向用户发送 JSON 响应。

如何设置Flask项目

我们要做的第一件事是创建一个 Flask 项目。如果你查看 Flask 的 官方文档 最小的应用程序 。

但是,我们不会遵循这一点。我们将编写一个更具扩展性且具有良好基础结构的应用程序。如果您愿意,可以按照 本指南 开始使用 Flask。

包中 的 core 。要将普通目录转换为 Python 包,我们只需包含一个 __init__.py 文件。因此,让我们首先创建核心包。

$ mkdir core

之后,让我们 __init__.py 在核心目录中创建文件:

$ cd core
$ touch __init__.py
$ cd ..

在项目的根目录中,创建一个名为 的文件 config.py 。我们将在此文件中存储项目的配置。在文件中,添加以下内容:

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

在上面的脚本中,我们创建了一个 的不同子类(根据不同的开发阶段) Config Config

请注意,我们已将 SECRET_KEY 设置为名为 SECRET_KEY 在根目录中 .env 创建一个名为的文件

APP_SETTINGS=config.DevelopmentConfig
SECRET_KEY=gufldksfjsdf

除了 SECRET_KEY ,我们还有 APP_SETTINGS ,它引用了我们在文件中创建的类之一 config.py 。我们将其设置为项目的当前阶段。

现在,我们可以在文件中添加以下内容 __init__.py

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='contact@ashutoshkrris.tk',
    doc='/',
    prefix='/api/v1'
)

在上面的 Python 脚本中,我们首先从已安装的 Flask 模块导入 Flask 类。接下来,我们创建 app Flask 类的对象。我们使用参数 __name__ 来指示应用程序的模块或包,以便 Flask 知道在哪里找到其他文件(例如模板)。

根据文件中的变量 将应用程序配置设置为 APP_SETTINGS .env

除此之外,我们还创建了一个 Api 类的对象。我们需要向其传递各种参数。我们可以在路由上找到 Swagger 文档 / /api/v1 每个 API 路由上都会加上前缀。

现在,让我们 routes.py 在包中创建一个文件 core 并添加以下命名空间:

from core import api
from flask import jsonify

ns = api.namespace('/', description='Horoscope APIs')

我们需要导入文件中的路线 __init__.py

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='contact@ashutoshkrris.tk',
    doc='/',
    prefix='/api/v1'
)

from core import routes			# Add this line

现在我们只剩下一个可以帮助我们运行 Flask 服务器的文件:

from core import app

if __name__ == '__main__':
    app.run()

一旦使用 python main.py 命令运行此文件,您将看到类似的输出:

Screenshot-2021-12-16-160820

现在,我们准备从星座运势网站上抓取数据。

如何从 Horoscope.com 抓取数据

如果您打开 Horoscope.com 并选择您的星座,就会显示您今天的星座运势数据。

Screenshot-2021-12-16-073450
Source: Horoscope.com

在上图中,您可以看到可以查看昨天、明天、每周、每月甚至自定义日期的星座运势。我们将使用所有这些。

但首先,如果你看到当前页面的 URL,它类似于: https://www.horoscope.com/us/horoscopes/general/horoscope-general-daily-today.aspx?sign=10 .

URL 中有两个变量,如果你看清楚了, 就是 的值 today sign sign 会根据星座来分配。变量 today 可以替换为 gone tomorrow .

下面的词典可以帮助我们了解星座:

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
}

这意味着如果你的星座是 摩羯座 那么 URL 中的 星座 10 .

接下来,如果我们希望获取自定义日期的星座数据,URL https://www.horoscope.com/us/horoscopes/general/horoscope-archive.aspx?sign=10&laDate=20211213 可以帮助我们。

它具有相同的 符号 变量,但它有另一个变量 laDate 的日期 YYYYMMDD

现在,我们准备创建不同的函数来获取星座数据。创建一个 utils.py 文件并继续操作。

如何获取当天的星座运势

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

我们创建了第一个接受两个参数的函数——一个整数 zodiac_sign 和一个字符串 day 。day 可以是今天、明天、昨天或今天之前的任何自定义日期,格式为 YYYY-MM-DD。

如果日期不是自定义日期,则其中不会有连字符 (-)。因此,我们为此设置了一个条件。

如果没有连字符,我们将对 发出 GET 请求 https://www.horoscope.com/us/horoscopes/general/horoscope-general-daily-{ day }.aspx?sign={ zodiac_sign } 。否则,我们首先将日期从 YYYY-MM-DD 更改为 YYYYMMDD 格式。

然后我们发出一个 GET 请求 https://www.horoscope.com/us/horoscopes/general/horoscope-archive.aspx?sign={ zodiac_sign }&laDate={ day } .

之后,我们使用 BeautifulSoup 从页面的响应内容中提取 HTML 数据。现在我们需要从此 HTML 代码中获取星座文本。如果您检查任何网页的代码,您会发现以下内容:

screenshot-2021-12-07-081318_nwhwwf

星座运势文本包含在 类为 div main-horoscope 。因此,我们使用 soup.find() 函数提取段落文本字符串并将其返回。

如何获取本周星座运势

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

上面的函数与前一个函数非常相似。我们只是将 URL 更改为 https://www.horoscope.com/us/horoscopes/general/horoscope-general-weekly.aspx?sign={ zodiac_sign } .

如何获取本月星座运势

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

此功能与其他两个功能类似,只是 URL 已更改为 https://www.horoscope.com/us/horoscopes/general/horoscope-general-monthly.aspx?sign={ zodiac_sign } .

如何创建 API 路由

我们将使用 Flask-RESTX 创建 API 路由。API 路由如下所示:

  • 对于每日或自定义日期: /api/v1/get-h或oscope/daily?day=today&sign=capricorn or api/v1/get-horoscope/daily?day=2022-12-14&sign=capricorn
  • 每周: api/v1/get-horoscope/weekly?sign=capricorn
  • 每月: api/v1/get-horoscope/monthly?sign=capricorn

URL 中有两个查询参数: 昨天 参数。day 参数 day 或自定义日期(如 2022-12-14)等值。sign sign 将采用星座名称,可以是大写或小写,无所谓。

为了从 URL 解析查询参数,Flask-RESTX 内置了对请求数据验证的支持,使用类似于 argparse reqparse 。 要在 URL 中添加参数,我们将使用 方法 add_argument RequestParser

parser = reqparse.RequestParser()
parser.add_argument('sign', type=str, required=True)

参数 type 将采用参数类型。这 required=True 使得查询参数必须传递。

现在,我们需要另一个查询参数 day。但此参数仅在每日星座运势 URL 中使用。

扩展解析器 copy() .

parser_copy = parser.copy()
parser_copy.add_argument('day', type=str, required=True)

不仅包含 parser_copy 日期 ,还 包含星座这正是我们每日星座运势所需要的。

Flask 可插入视图 之上 ,您只需在资源上定义方法即可轻松访问多种 HTTP 方法。

让我们创建 DailyHoroscopeAPI 类,该类继承 Resource 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')

装饰 @ns.route() 器设置 API 路由。在 DailyHoroscopeAPI 类中,我们有 get 方法来处理 GET 请求。 @ns.doc() 装饰器将帮助我们在 URL 上添加查询参数。

为了获取查询参数的值,我们将使用 parse_args() 方法,它将返回如下字典:

{'sign': 'capricorn', 'day': '2022-12-14'}

day 获取值 sign .

正如开头所定义的,我们将有一个 ZODIAC_SIGNS 字典。我们使用 try-except 块来处理请求。如果字典中没有该星座, KeyError 进行响应 NotFound

此外,如果 参数 参数中有连字符,我们会尝试将日期格式与 YYYY-MM-DD 匹配。如果不是这种格式,我们会引发 BadRequest 错误(错误 400)。如果 day 不包含连字符,我们会直接使用 get_horoscope_by_day() sign day day 方法

如果传递了一些乱码作为参数值, 则会引发 AttributeError BadRequest 错误。

另外两个路由也与上面的路由非常相似。不同之处在于,这里我们不需要 day 参数。因此, parser_copy 我们 parser 在这里使用 而不是 。

@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.')

现在我们的路线已经完成。要测试 API,您可以使用路线上提供的 Swagger 文档 / ,也可以使用 Postman 。让我们运行服务器并进行测试。

您还可以在公共服务器上部署项目,以便其他开发人员也可以访问和使用该 API。

包起来

在本教程中,我们学习了如何使用请求和 Beautiful Soup 从网站抓取数据。然后我们使用 Flask 和 Flask-RESTX 创建了一个 API。

如果你想了解如何使用 Python 与 API 交互,请查看 本指南 .

希望您喜欢它——感谢您的阅读!

教程代码: https://github.com/ashutoshkrris/Horoscope-API

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部