13.1 Webhose API
Webhose 是一项在线服务,其功能是汇集众多在线源,实时整理信息。借助Webhose API,我们能以编程的方式查询Webhose,得到JSON格式的结果。返回的数据经过JSON解析器解析后,在应用的模板上显示出来。
注册 Webhose API 密钥
登录https://www.webhose.io注册一个临时账号,邮件激活账号后,登录如下:
记下API秘钥后,点击顶部导航栏中的“API Playground”链接。这个页面供您试用Webhose API接口。(1)在“Define the query”下面的方框中输入一个词条。(2)在“Sort By”下拉菜单中选择“Relevancy”。(3)“Crawled Since”用于设定返回什么时候爬取的结果,默认值3天就可以了。(4)点击“Run”按钮。稍等片刻页面就会出现查询结果。
看一下Webhose API返回的结果,以及原始的JSON响应。向上拉动滚动条,找到“Endpoint”框,就可以找到URL。http://webhose.io/filterWebContent?token=<KEY&format=json&sort=relevancy&q=< QUERY>
13.2 添加搜索功能
- rango/webhose_search.py
import json
import urllib.parse
import urllib.request
def read_webhose_key():
webhose_api = None
try:
with open('search.key','r') as f:
webhose_api_key = f.readline().strip()
except:
raise IOError('search.key file not found')
return webhose_api_key
def run_query(serch_terms,size=10):
webhose_api_key = read_webhose_key()
if not webhose_api_key:
raise KeyError('Webhose key not found')
root_url = 'http://webhose.io/search'
query_string = urllib.parse.quote(search_terms)
search_url = ('{root_url}?token={key}&format=json&q={query}'
'&sort=relevancy&size={size}').format(
root_url=root_url,
key=webhose_api_key,
query=query_string,
size=size)
results = []
try:
response = urllib.request.urlopen(search_url).read().decode('utf-8'
json_response = json.loads(response)
for post in json_response['posts']:
results.append({
'title':post['title'],
'link':post['url'],
'summary':post['text'][:200]})
except:
print("Error when querying the webhose API")
return results
read_webhose_key():读取 Webhose API 密钥
read_webhose_key()函数从名为search.py的文件中读取Webhose API密钥。这个文件应该放在Django项目的根目录中(< workspace>/tango_with_django_project/),而不是Rango应用的目录中。
run_query():执行查询
run_query()函数接受两个参数:search_terms,值为字符串,表示用户输入的搜索词条;size,默认值为10,控制Webhose API返回的结果数量。run_query()函数与Webhose API通信,返回一个由Python字典构成的列表,每个字典表示一个结果(有title、link和summary)。
run_query()函数的逻辑大致可以分成7个任务,说明如下:
(1)调用 read_webhose_key() 获取Webhose API。
(2)然后构建要发给 API 的查询字符串。这里要编码URL,把特殊的字符串转化为Web服务器和浏览器能理解的格式。比如,空格会转换成%20
(3)根据 Webhose API 文档,接下来要拼接编码后的 search_terms 字符串和 size 参数,以及Webhose API 密钥,构建请求 Webhose API 的完整 URL。
(4)使用 Python urllib模块连接Webhose API。服务器响应存为response变量。
(5)使用 Python json 库把响应转换成 Python 字典对象。
(6)迭代字典,把Webhose API返回的各个结果(包含title、link和summay键值对)以字典为单位存入results列表
(7)返回 results 列表。
13.3 集成到 Rango 应用中
在 webhose_search.py 模块中实现搜索功能后,接下来分三步将它集成到Rango应用中。
(1)创建 search.html 模板,拓展自base.html 模板。search.html 模板有个HTML表单。
(2)编写一个Django视图,调用前面定义的 run_query() 函数,渲染 search.html 模板。
(3)在 Rango 应用的url.py模块中把新视图映射到一个 URL 上。
创建模板
- teplates/rango/rango/search.html
{
% extends 'rango/base.html' %}
{
% load static %}
{
% block title_block %}
Search
{
% endblock %}
{
% block body_block %}
<div>
<h1>Search with Rango</h1>
<br/>
<form class="form-inline" id="user_form"
method="post" action="{% url 'search' %}">
{
% csrf_token %}
<div class="form-group">
<input class="form-control" type="text" size="50"
name="query" value="" id="query" />
<button class="btn btn-primary" type="submit" name="submit"
value="Search">Search</button>
</div>
</form>
<div>
{
% if result_list %}
<h3>Results</h3>
<div class="list-group">
{
% for result in result_list %}
<div class="list-group-item">
<h4 class="list-group-item-heading">
<a href="{
{ result.link }}">{
{
result.title }}</a>
</h4>
<p class="list-group-item-text">{
{
result.summary }}</p>
</div>
{
% endfor %}
</div>
{
% endif %}
</div>
</div>
{
% endblock %}
上述模板代码主要执行两个任务:
(1) 在HTML表单中显示搜索框和搜索按钮,供用户输入和提交查询词条。
(2) 渲染模板时,如果传给模板上下文的results_list对象有内容,那就迭代results_list对象,渲染里面的结果。上述模板期待每个结果中有title、link和summary,这与前面定义的run_query()函数是一致的。
编写视图
Rango/views.py
def search(request):
result_list = []
if request.method =='POST':
query = request.POST['query'].strip()
if query:
# 调用前面定义的函数向 Webhose 发起查询,获得结果列表
result_list = run_query(query)
return render(request,'rango/search.html',{
'result_list':result_list})
添加映射
❏ 把search() 视图映射到 URL /rango/search/ 上,并设定 name=‘search’ 参数。在 Rango应用的 urls.py 模块中添加 url(r’search/$’, views.search, name=‘search’)。
- Rango/urls.py
urlpatterns = [
url(r'^$',views.index,name='index'),
url(r'^add_category/$',views.add_category,name='add_category'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$',views.show_category, name='show_category'),
url(r'about/$', views.about, name='about'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/add_page/$', views.add_page, name='add_page'),
url(r'^register/$',views.register,name='register'),
url(r'^login/$',views.user_login, name='login'),
url(r'^restricted/',views.restricted,name='restricted'),
url(r'^logout/$',views.user_logout,name='logout'),
url(r'search/$', views.search, name='search'),
]
❏ 更新 base.html 模板中的导航栏,加入搜索页面的链接。不要在模板中硬编码 URL,应该使用 url 模板标签。
- base.html
{
% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'add_category' %}">Add a New Category</a>
</li>
<li class="nav-item">
</li>
<li class="nav-item">
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}?next=/rango/">Logout</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'search' %}">Search</a>
</li>
练习
我的小结
-
NameError at /rango/search/,name ‘run_query’ is not defined
解决措施:from rango.webhose_search import read_webhose_key,run_query -
OSError at /rango/search/,search.key file not found
解决措施:取得Webhose的API接口的密钥
我的尝试:
因为暂时没有Webhose的API接口,改用高德地图API。
rango/webhose_search.py
import json
import urllib.parse
import urllib.request
from bs4 import BeautifulSoup
import requests
def read_webhose_key():
#webhose_api = None
#try:
# with open('search.key','r') as f:
# webhose_api_key = f.readline().strip()
#except:
# raise IOError('search.key file not found')
webhose_api_key = '8c7d5f869c745fca4ea8da92574a93d1'
return webhose_api_key
def run_query(serch_terms,size=10):
webhose_api_key = read_webhose_key()
if not webhose_api_key:
raise KeyError('Webhose key not found')
parameters = {
'address': serch_terms,
'key': '8c7d5f869c745fca4ea8da92574a93d1'
}
base = 'http://restapi.amap.com/v3/geocode/geo'
results = []
#try:
response = requests.get(base, parameters)
answer = response.json()
#print(answer)
try:
for post in answer['geocodes']:
results.append({
'title':post['formatted_address'],
'link':post['citycode'],
'summary':post['location'][:200]})
except:
print("Error when querying the webhose API")
#print(results)
return results
def main():
serch_terms = '中山港麦当劳'
print(run_query(serch_terms,size=10))
if __name__ == '__main__':
main()
改后的页面如下: