百度地图api多线程爬虫编写

最近要做一些数据爬取的工作,需要拿一些地理位置数据的信息,自然而然先使用百度的开放api来作为爬虫的获取对象。自己写了个保存数据到mongodb数据库的python爬虫,放在博客上记录一下。

先放全局变量的部分,是一些必要信息:

import requests
import json
import pymongo
from time import sleep
import threading

#参数
# query=药店    想查询的内容
# region=北京   想查询的城市
# page_num={}   页码, 意思是第几页
# page_size=20     每页显示的个数
# ak=你的百度地图api授权码
queries = ["客栈", "酒店", "旅馆", "民宿", "宾馆"]
cities = ["北京","上海","广州"]
ak = "百度地图api授权码"
base_url = 'http://api.map.baidu.com/place/v2/search?query={}®ion={}&output=json&output=json&ak={}&page_num={}&page_size=50&scope=2'
#连接mongo
client = pymongo.MongoClient(host='localhost', port=27017)
db = client.hotels
collection = db.hotel

因为要获取酒店相关信息,而百度的查询工具是关键词查询,所以相关的民宿、旅店也要放进去(百度不支持多参数)作为一个列表循环查找。base_url作为模板url,各个参数可以在官方文档中查到。

在这里使用了mongodb数据库作为输出源,插件用pymongo。

接下来是主要函数部分:

def getUrl(query, city, i):
    url = base_url.format(query, city, ak, i)
    return url

def getData(url):
    response = requests.get(url)               # 对网页进行请求
    jsondata = json.loads(response.text)       # 对下载的内容进行loads
    for j in jsondata['results']:
        #print(j,'\n')
        result = {
            'name': j.get('name'),
            'tag': j['detail_info'].get('tag'),
            'lat': j['location'].get('lat'),
            'lng': j['location'].get('lng'),
            'address': j.get('address'),
            'province': j.get('province'),
            'city': j.get('city'),
            'area': j.get('area'),
            'tel': j.get('telephone') if j.get('telephone') else 0,
            'score': j['detail_info'].get('overall_rating') if j['detail_info'].get('overall_rating') else 0
        }
        print(result, '\n')
        collection.update({'name': result['name']}, {
                          '$set': result}, True)  # 数据库去重
    return jsondata["results"]

为了后面的多线程调用的更方便点,把getUrl和getData拆开写了,百度api单个网页上有一定数量的json格式数据,因此先循环获得多个url,再用getData拿到详细数据。

result是最后获得的数据格式,collection.update而不是insert是为了完成数据的去重,这里是用数据的name字段来判断数据库中是否已含有该数据条,用set形式保存。

接下来就是把代码打包进多线程中,这里写了一个run函数来引用线程:

def run(city,query):
    for i in range(0, 10):  # 循环请求
        url = getUrl(query, city, i)
        t = threading.Thread(target=getData, args=(url,)) #加入了多线程
        t.setDaemon(True)
        t.start()
        if not getData(url):
            break
        #print("========================",res,"========================")
        sleep(1)  # 防止被阻止访问,sleep一下
        t.join()

setDaemon(True) 可以提供一个守护线程,因为轮询了10页的数据,而很多情况下数据只出现在前7页,所以需要在获取到空数据时把当前线程取消,免得有源源不断的新线程。。。

最后放上完整代码:

import requests
import json
import pymongo
from time import sleep
import threading

#参数
# query=药店    想查询的内容
# region=北京   想查询的城市
# page_num={}   页码, 意思是第几页
# page_size=20     每页显示的个数
# ak=你的百度地图api授权码
queries = ["客栈", "酒店", "旅馆", "民宿", "宾馆"]
cities = ["北京","上海","广州"]
ak = ""
base_url = 'http://api.map.baidu.com/place/v2/search?query={}®ion={}&output=json&output=json&ak={}&page_num={}&page_size=50&scope=2'
#连接mongo
client = pymongo.MongoClient(host='localhost', port=27017)
db = client.hotels
collection = db.hotel

def getUrl(query, city, i):
    url = base_url.format(query, city, ak, i)
    return url

def getData(url):
    response = requests.get(url)               # 对网页进行请求
    jsondata = json.loads(response.text)       # 对下载的内容进行loads
    for j in jsondata['results']:
        #print(j,'\n')
        result = {
            'name': j.get('name'),
            'tag': j['detail_info'].get('tag'),
            'lat': j['location'].get('lat'),
            'lng': j['location'].get('lng'),
            'address': j.get('address'),
            'province': j.get('province'),
            'city': j.get('city'),
            'area': j.get('area'),
            'tel': j.get('telephone') if j.get('telephone') else 0,
            'score': j['detail_info'].get('overall_rating') if j['detail_info'].get('overall_rating') else 0
        }
        print(result, '\n')
        collection.update({'name': result['name']}, {
                          '$set': result}, True)  # 数据库去重
    return jsondata["results"]

def run(city,query):
    for i in range(0, 10):  # 循环请求
        url = getUrl(query, city, i)
        t = threading.Thread(target=getData, args=(url,)) #加入了多线程
        t.setDaemon(True)
        t.start()
        if not getData(url):
            break
        #print("========================",res,"========================")
        sleep(1)  # 防止被阻止访问,sleep一下
        t.join()


if __name__ == "__main__":
    #页数i
    for city in cities:
        for query in queries:
            run(city,query)
    client.close()

本身百度api会有访问限制,普通用户每5秒都有获取数据的限制,没有用多线程时不得不每次都sleep(5)。。。改用多线程后就非常快啦

 

你可能感兴趣的:(Python,python,爬虫)