芜湖,感谢各位的支持。那么废话不多说开始我们今天的学习。
前提条件是:安装Fiddler的机器,跟智能手机 在同一个网络里, 否则智能手机不能把HTTP发送到Fiddler的机器上来。
下面来设置Android设备上的代理服务器:
打开android设备的“设置”->“WLAN”,找到你要连接的网络,在上面长按,然后选择“修改网络”,弹出网络设置对话框,在接下来弹出的对话框中,勾选“显示高级选项”。在接下来显示的页面中,点击“代理”,选择“手动”。
在“代理”后面的输入框选择“手动”,在“代理服务器主机名”后面的输入框输入电脑的ip地址,在“代理服务器端口”后面的输入框输入8888,然后点击“保存”按钮。
代理服务器主机名设为PC的IP,代理服务器端口设为Fiddler上配置的端口8888,点"保存"。
打开IPhone, 找到你的网络连接, 打开HTTP代理, 输入Fiddler所在机器的IP地址(比如:192.168.1.104) 以及Fiddler的端口号8888
为了让Fiddler能捕获HTTPS请求。如果你只需要截获HTTP请求, 可以忽略这一步
上面的设置还不能抓像招商银行、支付宝等APP的https包,因为这些APP对https证书进行了校验,还需要将Fiddler代理服务器的证书导到Android设备上才能抓这些APP的包
导入的过程:
打开浏览器,在地址栏中输入代理服务器的IP和端口,会看到一个Fiddler提供的页面:
点击页面中的“FiddlerRootcertificate”链接,接着系统会弹出对话框:
输入一个证书名称,然后直接点“确定”就好了。
用完了, 记得把IPhone上的Fiddler代理关闭, 以免IPhone上不了网。
app开发测试为了调试方便,通常会在电脑上装一些android模拟器,开多台进行测试。调试中通常要干的一件事就是抓取,那么想要抓包,我们必须要设置代理。
夜神android模拟机设置代理的方法:
进入列表后,鼠标点击wifi位置,长按左键,会出现一个修改网络的弹窗,如下图:
点击上图中的“修改网络”,会出现下图中的弹窗,勾选“显示高级选项”,
接着一切都明了了,代理服务器主机名填写你电脑的ip就行了(window系统的话,用ipconfig查看),接着再填写端口
环境:
针对美团版本5.4
新建一个spider
scrapy genspider -t basic Meituan_City meituan.com
编辑items.py
class MeituanCity(Item):
data = Field()
编辑 Meituan_City.py
# -*- coding: utf-8 -*-
import scrapy
import json
from tutorial.items import MeituanCity
class MeituanCitySpider(scrapy.Spider):
name = "Meituan_City"
allowed_domains = ["meituan.com"]
start_urls = (
'http://api.mobile.meituan.com/group/v1/city/list?show=all',
)
def parse(self, response):
data = json.loads(response.body)
for item in data['data']:
cityId = item['id']
# http://api.mobile.meituan.com/group/v2/area/list?cityId=42&spatialFields=center
url = 'http://api.meituan.com/group/v2/area/list?cityId=%s&spatialFields=center' % cityId
print url
yield scrapy.Request(
url,
callback=self.Parse_Geo,
meta={'item': item}
)
break
def Parse_Geo(self, response):
print response.url
data = json.loads(response.body)
metaitem = response.meta['item']
# 商区信息
subareasinfo = dict()
if 'data' in data:
if 'subareasinfo' in data['data']:
for item in data['data']['subareasinfo']:
subareasinfo[item['id']] = item
if 'data' in data:
if 'areasinfo' in data['data']:
for line in data['data']['areasinfo']:
# 行政区
districtName = line['name']
districtId = line['id']
for tmp in line['subareas']:
# 商圈信息
area = subareasinfo[tmp]
center = area['center']
center = center.replace('POINT(', '').replace(')', '').split()
if len(center) > 1:
lat = center[1]
lng = center[0]
longitude = None
latitude = None
try:
longitude = str(int(float(lng) * 1000000))
latitude = str(int(float(lat) * 1000000))
except:
pass
Item = MeituanCity()
Item['data'] =dict()
geoItem=Item['data']
# 城市信息
geoItem['cityid'] = metaitem['id']
geoItem['cityname'] = metaitem['name']
# 行政区
geoItem['districtId'] = districtId
geoItem['districtName'] = districtName
# 商圈
geoItem['SubAreaId'] = area['id']
geoItem['secondArea'] = area['name']
# 经纬度
geoItem['longitude'] = longitude
geoItem['latitude'] = latitude
yield Item
此时运行:
scrapy runspider tutorial/spiders/Meituan_City.py
把上节内容生成的城市信息 items.json改成city_items.json作为第二部分爬虫的启动数据
添加items.py
class MeituanItem(Item):
data = Field()
创建模板:
scrapy genspider -t basic Meituan_meishi meituan.com
添加以下代码到Meituan_meishi.py
# -*- coding: utf-8 -*-
import scrapy
import codecs
import json
from tutorial.items import MeituanItem
import re
class MeituanMeishiSpider(scrapy.Spider):
'''
美食团购页面信息采集
'''
name = "Meituan_meishi"
allowed_domains = ["meituan.com"]
'''
start_urls = (
'http://www.meituan.com/',
)
'''
offset = 0
def start_requests(self):
file = codecs.open('city_items.json', 'r', encoding='utf-8')
for line in file:
item = json.loads(line)
cityid = item['data']['cityid']
latitude = item['data']['latitude']
longitude= item['data']['longitude']
lat = round(float(latitude), 6)
lng= round(float(longitude), 6)
url = 'http://api.mobile.meituan.com/group/v4/deal/select/city/42/cate/1?sort=defaults&mypos='+ str(lat) +'%2C'+ str(lng) +'&offset=0&limit=15'
yield scrapy.Request(url,callback=self.parse)
break
file.close()
def parse(self, response):
'''
数据存储以及翻页操作
'''
item = MeituanItem()
data = json.loads(response.body)
item['data']=dict()
item['data'] = data
yield item
offset = re.search('offset=(\d+)',response.request.url).group(1)
url = re.sub('offset=\d+','offset='+str(int(offset)+15),response.request.url)
yield scrapy.Request(url,callback=self.parse)
运行:
scrapy runspider tutorial/spiders/Meituan_meishi.py
在爬虫系统中,待抓取URL队列是很重要的一部分。待抓取URL队列中的URL以什么样的顺序排列也是一个很重要的问题,因为这涉及到先抓取那个页面,后抓取哪个页面。而决定这些URL排列顺序的方法,叫做抓取策略。
1、 深度优先搜索策略(顺藤摸瓜)(Depth-First Search)
即图的深度优先遍历算法。网络爬虫会从起始页开始,一个链接一个链接跟踪下去,处理完这条线路之后再转入下一个起始页,继续跟踪链接。
2、 广度(宽度)优先搜索策略(Breadth First Search)
宽度优先遍历策略的基本思路是,将新下载网页中发现的链接直接插入待抓取URL队列的末尾。也就是指网络爬虫会先抓取起始网页中链接的所有网页,然后再选择其中的一个链接网页,继续抓取在此网页中链接的所有网页。
有很多研究将广度优先搜索策略应用于聚焦爬虫中。其基本思想是认为与初始URL在一定链接距离内的网页具有主题相关性的概率很大。
深度优先搜索算法涉及的是堆栈
广度优先搜索涉及的是队列。
堆栈(stacks)具有后进先出(last in first out,LIFO)的特征
队列(queue)是一种具有先进先出(first in first out,LIFO)特征的线性数据结构
默认情况下,Scrapy使用 LIFO 队列来存储等待的请求。简单的说,就是 深度优先顺序 。深度优先对大多数情况下是更方便的。如果您想以 广度优先顺序 进行爬取,你可以设置以下的设定:
DEPTH_PRIORITY = 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
深度优先搜索算法(Depth First Search),是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。
如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。
如右图所示的二叉树:
A 是第一个访问的,然后顺序是 B、D,然后是 E。接着再是 C、F、G。
那么,怎么样才能来保证这个访问的顺序呢?
分析一下,在遍历了根结点后,就开始遍历左子树,最后才是右子树。
因此可以借助堆栈的数据结构,由于堆栈是后进先出的顺序,由此可以先将右子树压栈,然后再对左子树压栈,
这样一来,左子树结点就存在了栈顶上,因此某结点的左子树能在它的右子树遍历之前被遍历。
深度优先遍历代码片段
//深度优先遍历
void depthFirstSearch(Tree root){
stack nodeStack; //使用C++的STL标准模板库
nodeStack.push(root);
Node *node;
while(!nodeStack.empty()){
node = nodeStack.top();
printf(format, node->data); //遍历根结点
nodeStack.pop();
if(node->rchild){
nodeStack.push(node->rchild); //先将右子树压栈
}
if(node->lchild){
nodeStack.push(node->lchild); //再将左子树压栈
}
}
}
完整代码:
/**
*
*/
#include
#include
#include
#include
#include
using namespace std;
#define Element char
#define format "%c"
typedef struct Node {
Element data;
struct Node *lchild;
struct Node *rchild;
} *Tree;
int index = 0; //全局索引变量
//二叉树构造器,按先序遍历顺序构造二叉树
//无左子树或右子树用'#'表示
void treeNodeConstructor(Tree &root, Element data[]){
Element e = data[index++];
if(e == '#'){
root = NULL;
}else{
root = (Node *)malloc(sizeof(Node));
root->data = e;
treeNodeConstructor(root->lchild, data); //递归构建左子树
treeNodeConstructor(root->rchild, data); //递归构建右子树
}
}
//深度优先遍历
void depthFirstSearch(Tree root){
stack nodeStack; //使用C++的STL标准模板库
nodeStack.push(root);
Node *node;
while(!nodeStack.empty()){
node = nodeStack.top();
printf(format, node->data); //遍历根结点
nodeStack.pop();
if(node->rchild){
nodeStack.push(node->rchild); //先将右子树压栈
}
if(node->lchild){
nodeStack.push(node->lchild); //再将左子树压栈
}
}
}
//广度优先遍历
void breadthFirstSearch(Tree root){
queue nodeQueue; //使用C++的STL标准模板库
nodeQueue.push(root);
Node *node;
while(!nodeQueue.empty()){
node = nodeQueue.front();
nodeQueue.pop();
printf(format, node->data);
if(node->lchild){
nodeQueue.push(node->lchild); //先将左子树入队
}
if(node->rchild){
nodeQueue.push(node->rchild); //再将右子树入队
}
}
}
/**
*
*/
#include "binarytree.h"
int main() {
//上图所示的二叉树先序遍历序列,其中用'#'表示结点无左子树或无右子树
Element data[15] = {'A', 'B', 'D', '#', '#', 'E', '#', '#', 'C', 'F','#', '#', 'G', '#', '#'};
Tree tree;
treeNodeConstructor(tree, data);
printf("深度优先遍历二叉树结果: ");
depthFirstSearch(tree);
printf("\n\n广度优先遍历二叉树结果: ");
breadthFirstSearch(tree);
return 0;
}