下载地址:https://www.pan38.com/share.php?code=pvvmX 提取码:7769
AutoJS实现的美团商家联系方式采集工具完整代码,包含UI交互、数据采集和导出功能,仅供学习参考哈,因为这个工具我还是花了不少时间开发的,之前自己本身就是做软件站的,所以几年的时间还是开发了不少好东西免费分享出来,最主要他支持多线程,采集效果方面绝对是优秀的哈,那么下面是一些核心的采集代码部分,仅供学习参考。
可视化UI界面设置采集参数
自动打开美团APP并执行搜索
智能识别商家名称和电话按钮
自动翻页采集多页数据
CSV格式数据导出功能
// 初始化UI界面
auto.waitFor();
ui.layout(
);
// 全局变量
let collectedData = [];
const MAX_COUNT = 500; // 最大采集数量
// 开始采集按钮事件
ui.start.click(() => {
let city = ui.city.text();
let keyword = ui.keyword.text();
if (!city || !keyword) {
toast("请填写城市和关键词");
return;
}
threads.start(function() {
ui.run(() => {
ui.start.enabled = false;
ui.progress.visibility = 0;
});
launchApp("美团");
sleep(3000);
// 搜索目标商家
click("美食");
sleep(1000);
setText(keyword + " " + city);
sleep(2000);
click("搜索");
sleep(3000);
// 滚动采集数据
let count = 0;
while (count < MAX_COUNT) {
let items = className("android.view.View").depth(15).find();
for (let i = 0; i < items.length; i++) {
let item = items[i];
let name = item.findOne(className("android.widget.TextView").depth(16));
let phoneBtn = item.findOne(textMatches("电话"));
if (name && phoneBtn) {
let shopName = name.text();
click(phoneBtn.bounds().centerX(), phoneBtn.bounds().centerY());
sleep(1000);
let phone = textMatches(/1[3-9]\d{9}/).findOne(2000);
if (phone) {
collectedData.push({
name: shopName,
phone: phone.text()
});
log("采集到: " + shopName + " - " + phone.text());
count++;
}
back();
sleep(500);
}
}
// 更新进度
ui.run(() => {
ui.progress.progress = (count / MAX_COUNT) * 100;
});
// 滚动加载更多
swipe(device.width / 2, device.height * 0.8,
device.width / 2, device.height * 0.2, 500);
sleep(2000);
}
ui.run(() => {
ui.start.enabled = true;
toast("采集完成,共采集" + count + "条数据");
ui.result.visibility = 0;
ui.result.setDataSource(collectedData);
});
});
});
// 导出数据按钮事件
ui.export.click(() => {
if (collectedData.length === 0) {
toast("没有可导出的数据");
return;
}
let csv = "商家名称,联系电话\n";
collectedData.forEach(item => {
csv += `"${item.name}","${item.phone}"\n`;
});
let path = "/sdcard/美团商家数据_" + new Date().getTime() + ".csv";
files.write(path, csv);
toast("数据已导出到:" + path);
});
代码语言:txt
AI代码解释
// 初始化UI界面
auto.waitFor();
ui.layout(
<card cardCornerRadius="8" margin="0 0 16 0">
<vertical padding="16">
<horizontal>
<text text="目标城市:" textSize="16sp" layout_weight="1"/>
<input id="city" hint="如:北京/上海" layout_weight="3"/>
</horizontal>
<horizontal>
<text text="行业分类:" textSize="16sp" layout_weight="1"/>
<input id="category" hint="如:餐饮/美容" layout_weight="3"/>
</horizontal>
<horizontal>
<text text="采集数量:" textSize="16sp" layout_weight="1"/>
<input id="count" text="50" inputType="number" layout_weight="3"/>
</horizontal>
<horizontal>
<text text="延迟设置:" textSize="16sp" layout_weight="1"/>
<seekbar id="delay" max="10" progress="3" layout_weight="3"/>
</horizontal>
</vertical>
</card>
<grid id="btnGrid" spanCount="2">
<button id="start" text="开始采集" bg="#FF5C35" textColor="#FFFFFF"/>
<button id="stop" text="停止采集" bg="#999999" textColor="#FFFFFF"/>
<button id="export" text="导出CSV" bg="#4CAF50" textColor="#FFFFFF"/>
<button id="clear" text="清空数据" bg="#F44336" textColor="#FFFFFF"/>
</grid>
<horizontal>
<text text="进度:" textSize="14sp"/>
<text id="progressText" text="0/0" textSize="14sp" layout_weight="1"/>
<text id="timeText" text="00:00:00" textSize="14sp"/>
</horizontal>
<progressbar id="progress" style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"/>
<list id="resultList" layout_weight="1">
<card cardCornerRadius="4" margin="4">
<vertical padding="8">
<text text="{
{shop}}" textSize="16sp" textColor="#333333"/>
<text text="{
{phone}}" textSize="14sp" textColor="#FF5C35"/>
<text text="{
{address}}" textSize="12sp" textColor="#666666"/>
<text text="{
{category}} | 评分:{
{rating}}" textSize="12sp" textColor="#888888"/>
</vertical>
</card>
</list>
</vertical>
);
// 全局变量
let collectedData = [];
let isRunning = false;
let startTime = 0;
let threadPool = [];
const MAX_THREADS = 3;
// 工具函数
function formatTime(seconds) {
let hrs = Math.floor(seconds / 3600);
let mins = Math.floor((seconds % 3600) / 60);
let secs = seconds % 60;
return ${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')};
}
function updateTimer() {
if (!isRunning) return;
let elapsed = Math.floor((new Date().getTime() - startTime) / 1000);
ui.run(() => ui.timeText.setText(formatTime(elapsed)));
setTimeout(updateTimer, 1000);
}
// 核心采集逻辑
function collectShopInfo(index, total) {
try {
// 智能定位商家元素
let shopItem = className("android.view.View").depth(15).findOnce(index);
if (!shopItem) {
swipe(device.width / 2, device.height 0.7,
device.width / 2, device.height 0.3, 500);
sleep(2000);
return;
}
let shopName = shopItem.findOne(className("android.widget.TextView").depth(16));
let phoneBtn = shopItem.findOne(textMatches("电话|联系"));
if (shopName && phoneBtn) {
// 点击电话按钮
click(phoneBtn.bounds().centerX(), phoneBtn.bounds().centerY());
sleep(1500);
// 多种方式提取电话
let phone = null;
let phoneElements = [
textMatches(/1[3-9]\d{9}/),
descMatches(/1[3-9]\d{9}/),
className("android.widget.TextView").textContains("电话")
];
for (let elem of phoneElements) {
let found = elem.findOne(2000);
if (found) {
phone = found.text().replace(/[^0-9]/g, '');
break;
}
}
// 获取其他信息
let address = shopItem.findOne(textMatches("地址|位置"))?.text() || "未知";
let rating = shopItem.findOne(textMatches(/[\d.]+\s*分/))?.text() || "0分";
if (phone) {
collectedData.push({
shop: shopName.text(),
phone: phone,
address: address,
category: ui.category.text(),
rating: rating
});
ui.run(() => {
ui.progressText.setText(`${collectedData.length}/${ui.count.text()}`);
ui.progress.progress = (collectedData.length / parseInt(ui.count.text())) * 100;
ui.resultList.setDataSource(collectedData);
});
}
back();
sleep(1000 + random(0, 2000) * ui.delay.progress / 10);
}
} catch (e) {
log("采集出错: " + e);
}
}
// 按钮事件
ui.start.click(() => {
if (isRunning) return;
let city = ui.city.text();
let category = ui.category.text();
if (!city || !category) {
toast("请填写城市和行业分类");
return;
}
collectedData = [];
isRunning = true;
startTime = new Date().getTime();
updateTimer();
// 启动美团APP
launchApp("美团");
sleep(3000);
// 执行搜索
click("美食");
sleep(1000);
setText(category + " " + city);
sleep(2000);
click("搜索");
sleep(5000);
// 多线程采集
let targetCount = parseInt(ui.count.text());
for (let i = 0; i < MAX_THREADS; i++) {
threadPool[i] = threads.start(function() {
while (isRunning && collectedData.length < targetCount) {
collectShopInfo(collectedData.length, targetCount);
}
});
}
});
ui.stop.click(() => {
isRunning = false;
threadPool.forEach(t => t.interrupt());
toast("采集已停止");
});
ui.export.click(() => {
if (collectedData.length === 0) {
toast("没有可导出的数据");
return;
}
let csv = "商家名称,联系电话,地址,行业分类,评分\n";
collectedData.forEach(item => {
csv += `"${item.shop}","${item.phone}","${item.address}","${item.category}","${item.rating}"\n`;
});
let timestamp = new Date().toISOString().replace(/[:.]/g, '-');
let path = `/sdcard/美团商家数据_${timestamp}.csv`;
files.write(path, csv);
toast("数据已导出到:" + path);
app.startActivity({
action: "android.intent.action.VIEW",
data: "file://" + path,
type: "text/csv"
});
});
ui.clear.click(() => {
collectedData = [];
ui.run(() => {
ui.progressText.setText("0/0");
ui.progress.progress = 0;
ui.resultList.setDataSource([]);
});
});