学習と研究のために抽出ツールを作成しましたが、実験では抽出効率が非常に高く、約3秒で20人のファンの情報を抽出できます。12W以上を抽出するのは夢ではありません。一日の情報。。。
現在、多くのクローラーはユーザーのファン情報を継続的にクロールできません。最初の数ページをクロールした後は、常にデータを取得できない状況が発生します。このツールは、この状況に合わせて特別に最適化されており、継続的に中断することはありません。クロールの目的は、新しいデバイス番号を登録することで解決することです。
上:
以前に使用したng +電子については、次のコードを投稿してください。
<div style="background-color:whitesmoke;width: 100%;height: 100%;">
<div style="height: 20%;line-height: 50px;text-align: center;background-color: #e0dbd2">
<label>采集哪个用户的,输入这个用户的UID:</label>
<input nz-input [(ngModel)]="uid" style="width:200px;" [disabled]="isRunning"/>
<label>代理IP:</label>
<input nz-input [(ngModel)]="pip" style="width:200px;"/>
<br/>
<button nz-button nzType="primary" (click)="startNewCollect(0)" style="margin-left: 15px;" [disabled]="isRunning">全新采集</button>
<button nz-button nzType="primary" (click)="startLastCollect()" style="margin-left: 15px;" [disabled]="isRunning">继续上次采集</button>
<button nz-button nzType="danger" (click)="stopCollect()" style="margin-left: 15px;">停止采集</button>
</div>
<div style="height: 70%;text-align: center;">
<nz-spin style="line-height: 200px;" nzSimple *ngIf="isRunning" [nzSize]="'large'" nzTip="自动采集中..."></nz-spin>
<label>已成功采集到{
{count}}个</label>
<button nz-button nzType="link" (click)="exportExcel()" style="margin-left: 15px;" [disabled]="isRunning">导出到excel中</button>
</div>
<div style="position:fixed;bottom: 0px;min-height: 10%;background-color: #e0dbd2;width: 100%;">
<span style="line-height: 50px;margin-left: 15px;color: red;">
</span>
</div>
</div>
import { Component,NgZone } from '@angular/core';
import { WebsqlService } from './service/websql.service';
import { HttpService } from './service/http.service';
import { NzMessageService } from 'ng-zorro-antd/message';
declare var XLSX: any;
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
token = "xxx";
money = "";
uid = "";
pip = "";
count = 0;
more = false;
cursor = 0;
isRunning = false;
constructor(
private _ngZone: NgZone,
private websqlService:WebsqlService,
private http:HttpService,
private message:NzMessageService){
this.websqlService.createTB();
this.getMoney();
this.getLastData();
}
getLastData(){
const me = this;
me.websqlService.findLastAll(function(data){
if(data&& data.rows&&data.rows.length>0){
me._ngZone.run(()=>{
me.uid = data.rows.item(0).uid;
me.count = data.rows.item(0).count;
me.more = data.rows.item(0).more;
me.cursor = data.rows.item(0).min;
});
}
});
}
getMoney(){
const me = this;
me.http.get("/v2/account/query",{token:me.token},
function(data){
me.money = data.money;
},function(msg){});
}
/**
* 全新开始
* @param cursor
*/
startNewCollect(cursor){
if(this.uid == ""){
this.message.create("error","请输入UID后再采集...");
return;
}
if(this.pip == ""){
this.message.create("error","请输入代理IP后再采集...");
return;
}
let param = {
token:this.token,
uid:this.uid,
cursor:cursor,
ip:this.pip
};
this.isRunning = true;
const me = this;
me.http.get("/v2/douyin/user/follower",param,
function(data){
if(data && data.data && data.data.msg=="No results."){
me.startNewCollect(0);
}else{
data.data.followers.forEach(fans => {
me.websqlService.insertUserValue(fans.uid,fans.short_id.length>2?fans.short_id:fans.unique_id);
});
me.websqlService.insertLastValue(me.uid,Number(me.count)+Number(data.data.followers.length),data.data.has_more,data.data.min_time);
me.getLastData();
if(data.data.has_more){
setTimeout(function(){
me.startLastCollect();
}, 1000)
}else{
me.isRunning = false;
}
}
},function(msg){
me.startNewCollect(0);
});
}
/**
* 继续上一次
*/
startLastCollect(){
this.getLastData();
if(this.uid == ""){
this.message.create("error","请输入UID后再采集...");
return;
}
if(this.pip == ""){
this.message.create("error","请输入代理IP后再采集...");
return;
}
this.getMoney();
if(!this.more){return;}
let param = {
token:this.token,
uid:this.uid,
cursor:this.cursor,
ip:this.pip
};
this.isRunning = true;
const me = this;
me.http.get("/v2/douyin/user/follower",param,
function(data){
if(data && data.data && data.data.msg=="No results."){
me.startLastCollect();
}else{
data.data.followers.forEach(fans => {
me.websqlService.insertUserValue(fans.uid,fans.short_id.length>2?fans.short_id:fans.unique_id);
});
me.websqlService.insertLastValue(me.uid,Number(me.count)+Number(data.data.followers.length),data.data.has_more,data.data.min_time);
me.getLastData();
if(data.data.has_more && me.isRunning){
setTimeout(function(){
me.startLastCollect();
}, 1000)
}else{
me.isRunning = false;
}
}
},function(msg){
me.startLastCollect();
});
}
/**
* 停止
*/
stopCollect(){
this.isRunning = false;
}
exportExcel(){
let list = [];
const me = this;
me.websqlService.findUserAll(function(data){
if(data&& data.rows&&data.rows.length>0){
for(var i=0;i<data.rows.length;i++){
var v = {"uid":data.rows.item(i).uid,"did":data.rows.item(i).sid}
list.push(v);
}
me.xlsxExportOrigenalToExcel(list);
}
}
);
}
xlsxExportOrigenalToExcel(data){
let ws = XLSX.utils.json_to_sheet(data);
let wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, '粉丝');
var oDay = new Date();
var tt = oDay.getFullYear()+"年"+(oDay.getMonth()+1)+"月"+oDay.getDate()+"日"+oDay.getHours()+"时"+oDay.getMinutes()+"分"+oDay.getSeconds()+"秒";
XLSX.writeFile(wb, tt+'导出' + ".xls");
this.websqlService.delUserRecord();
}
}
主に途中で中断がなく、プロキシIPを中断することなく変更できるため、完璧です。