一、JS客户端单页爬虫

1、定位爬取标签

因为该爬虫是要覆盖不同网页,所以不能通过特殊标识来定位标签,只能将所有的a标签爬出来然后对href的内容进行一定的筛选。

//预定列表头
var linklist = [['链接']]
//获取所有的a标签
var links = document.getElementsByTagName('a');  
for(var link of links){
    //筛选出以https://chat.whatsapp.com/为开头的a标签
    if (link.href.startsWith('https://chat.whatsapp.com/')){
        //将符合要求的链接添加进列表
        linklist.push([link.href]);
    }
}
//使用set对列表元素去重
linklist = [...new Set(linklist)]

2、使用xlsx.js工具包

xlsx.js工具包可以直接在前端通过js将数据保存为excel文件。

(1)动态载入js文件
var myScript= document.createElement("script");
myScript.type = "text/javascript";
//在此处引用了其他网站的xlsx.js包,不安全,不确定会不会失效
myScript.src = 'https://demo.haoji.me/2017/02/08-js-xlsx/js/xlsx.core.min.js';
document.body.appendChild(myScript);
(2)定义工具函数

sheet2blob

function sheet2blob(sheet, sheetName) {
    sheetName = sheetName || 'sheet1';
    var workbook = {
        SheetNames: [sheetName],
        Sheets: {}
    };
    workbook.Sheets[sheetName] = sheet;
    var wopts = {
        bookType: 'xlsx', 
        bookSST: false, 
        type: 'binary'
    };
    var wbout = XLSX.write(workbook, wopts);
    var blob = new Blob([s2ab(wbout)], {type:"application/octet-stream"});
    function s2ab(s) {
        var buf = new ArrayBuffer(s.length);
        var view = new Uint8Array(buf);
        for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
        return buf;
    }
    return blob;
}

openDownloadDialog

function openDownloadDialog(url, saveName) {
    if(typeof url == 'object' && url instanceof Blob)
    {
        url = URL.createObjectURL(url);
    }
    var aLink = document.createElement('a');
    aLink.href = url;
    aLink.download = saveName || '';
    var event;
    if(window.MouseEvent) event = new MouseEvent('click');
    else
    {
        event = document.createEvent('MouseEvents');
        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    }
    aLink.dispatchEvent(event);
}
(3)保存为excel

保存excel操作的前提是需要xlsx.js工具包已经加载完成,所以保存文件的操作需要在onload函数内使用。

myScript.onload = function (){
    var sheet = XLSX.utils.aoa_to_sheet(linklist);
	openDownloadDialog(sheet2blob(sheet), 'grouplink.xlsx');
}

3、完整代码

var myScript= document.createElement("script");
myScript.type = "text/javascript";
myScript.src = 'https://demo.haoji.me/2017/02/08-js-xlsx/js/xlsx.core.min.js';
document.body.appendChild(myScript);
myScript.onload = function (){
	function sheet2blob(sheet, sheetName) {
		sheetName = sheetName || 'sheet1';
		var workbook = {
			SheetNames: [sheetName],
			Sheets: {}
		};
		workbook.Sheets[sheetName] = sheet;
		var wopts = {
			bookType: 'xlsx', 
			bookSST: false, 
			type: 'binary'
		};
		var wbout = XLSX.write(workbook, wopts);
		var blob = new Blob([s2ab(wbout)], {type:"application/octet-stream"});
		function s2ab(s) {
			var buf = new ArrayBuffer(s.length);
			var view = new Uint8Array(buf);
			for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
			return buf;
		}
		return blob;
	}
	function openDownloadDialog(url, saveName) {
		if(typeof url == 'object' && url instanceof Blob)
		{
			url = URL.createObjectURL(url);
		}
		var aLink = document.createElement('a');
		aLink.href = url;
		aLink.download = saveName || '';
		var event;
		if(window.MouseEvent) event = new MouseEvent('click');
		else
		{
			event = document.createEvent('MouseEvents');
			event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
		}
		aLink.dispatchEvent(event);
	}
	var linklist = [['链接']]
	var links = document.getElementsByTagName('a');
	for(var link of links){
		if (link.href.startsWith('https://chat.whatsapp.com/')){
			linklist.push([link.href]);
		}
	}
	linklist = [...new Set(linklist)]
	var sheet = XLSX.utils.aoa_to_sheet(linklist);
	openDownloadDialog(sheet2blob(sheet), 'grouplink.xlsx');
}

二、Google搜索结果爬虫

服务端爬虫使用了python的scrapy爬虫框架,该框架封装了一些爬虫方法,可以比较方便的完成爬虫工作。

1、创建scrapy项目

在linux上安装scrapy十分容易,但在windows上安装可能会遇到Microsoft Visual C++ 14.0 is required这样的报错,解决办法见Microsoft Visual C++ Build Tools下载/解决Visual C++ 14.0 is required问题

接下来按照官方教程创建grouplink爬虫项目。

scrapy startproject grouplink
cd grouplink

2、实现Google爬虫

本次爬虫的需求是通过运营给定的一系列关键词从Google的搜索结果的网页中爬取WhatsApp的群链接。这就涉及到两个点,第一是对Google搜索结果网页的爬取,第二是对搜索结果网页中的群链接爬取,最后的结果只需得到群连接。

(1)定义Item

item的作用在我的理解上来说是将爬虫爬取到的数据暂存下来,方便以后使用。

items.py

import scrapy
class GrouplinkItem(scrapy.Item):
    link = scrapy.Field()
(2)新建grouplinkspider.py文件

在grouplink项目下的spiders文件夹新建grouplinkspider.py,搭建爬虫程序的基础架子。

import scrapy
from scrapy.http import Request

class GrouplinkspiderSpider(scrapy.Spider):
    name = 'grouplinkspider'
    allowed_domains = []
    start_urls = []
    def parse(self, response):
        pass
(2)分析Google搜索结果页,编写对应爬虫

通过对Google搜素结果页源码的分析,发现我们所需要的网页a标签并没有任何特殊性的标识,所以用最粗暴的办法将所有的a标签都爬下来然后对href字段进行过滤。

def parse(self, response):
     urls = response.xpath('//a/@href')
     for url in urls:
     	strurl = url.extract()
        //筛选除链接中包含http且不包含google字段的链接
        if 'http' in strurl and 'google' not in strurl:
        	//使用Request方法执行进一步爬虫
            yield Request(strurl,callback=self.getGroup)

接下来对系列关键词和搜索结果分页的循环爬取。

新建keywords.py文件。

keywords = []
def getKeyWords(index):
    return keywords[index]

def getKeyWordsCount():
    return len(keywords)

引入keywords,并依次分页循环爬虫方法。

from .keywords import getKeyWords,getKeyWordsCount
class GrouplinkspiderSpider(scrapy.Spider):
    name = 'grouplinkspider'
    allowed_domains = []
    url = "https://www.google.com/search?q="
    keywordcount = 0
    start_urls = [url+getKeyWords(0)+"&start=0"]
    count = 0
    //爬取最大页数
    maxCount = 10

    def parse(self, response):
        //在日志文件中打印出爬取的url
        self.logger.info('Parse function called on %s', response.url)
        urls = response.xpath('//a/@href')
        for url in urls:
            strurl = url.extract()
            if 'http' in strurl and 'google' not in strurl:
                yield Request(strurl,callback=self.getGroup)
        //当页数小于最大页数时加1,并循环执行爬虫函数
        if self.count < self.maxCount :
            self.count += 1
            yield Request(self.url+getKeyWords(self.keywordcount)+"&start="+str(self.count*10),callback=self.parse)
        //依次获取keywords,将页数置零,并循环执行爬虫函数
        elif self.keywordcount < getKeyWordsCount(): 
            self.count = 0
            self.keywordcount += 1 
            yield Request(self.url+getKeyWords(self.keywordcount)+"&start=0",callback=self.parse)

最后对搜索结果网页群链接的爬取,由于每个网页的结构都不一样,所以也只能将所有的a标签爬取下来,然后对href进行过滤。

import re
from grouplink.items import GrouplinkItem
def getGroup(self, response):
        links = response.xpath('//a/@href')
        for link in links:
            strlink = link.extract()
            //过滤出包含https://chat.whatsapp.com/字段的链接
            if "https://chat.whatsapp.com/" in strlink:
                //使用正则切出有用的链接
                pattern = re.compile(r"https://chat.whatsapp.com/invite/.{22}|https://chat.whatsapp.com/.{22}")
                strlinks = pattern.findall(strlink)
                for substrlink in strlinks:
                    //实例化item
                    item = GrouplinkItem()
                    item['link'] = substrlink
                    yield item

3、数据保存

现在我们需要的群链接已经爬取下来,然后只需要保存就行了,由于业务需求暂时只保存成excel文件,接下来会保存到后台数据库中。

pipelines.py

from openpyxl import Workbook

class GrouplinkPipeline(object):
    def __init__(self):
        self.wb = Workbook()
        self.ws = self.wb.active
        self.ws.append(['链接'])

    def process_item(self, item, spider):
        data = [item['link']]
        self.ws.append(data)
        self.wb.save('grouplink.xlsx')
        return item

setting.py

ITEM_PIPELINES = {
   'grouplink.pipelines.GrouplinkPipeline': 300,
}

4、(附)对于setting.py的配置

在爬取Google的时候遇到了机器人协议文件的限制,需要在setting文件中关闭机器人协议。

ROBOTSTXT_OBEY = False

对于输出日志的配置。

to_day = datetime.datetime.now()
log_file_path = "log/scrapy_{}_{}_{}.log".format(to_day.year,to_day.month,to_day.day)
LOG_LEVEL = "DEBUG"
LOG_FILE = log_file_path

反爬的一些措施。

//设置访问超时时间
DOWNLOAD_TIMEOUT = 200
//设置访问延时
DOWNLOAD_DELAY = 10
//设置不需要cookie
COOKIES_ENABLED = False

5、(附)简单粗暴的断点续爬

断点续爬是爬虫项目中比较重要的功能,一般的是实现方式是用数据库记录爬虫进度信息,具体实现参见网上的教程。在这个项目中,使用了一种简单粗暴的方式来实现断点续爬,将数据保存在文件中,每次爬虫开始和结束对文件的数据进行操作,具体实现如下。

(1)初始化数据

grouplinkspider.py

def __init__(self):                                  #创建初始化函数
    with open('./count.json', 'r') as f:             #以只读的方式打开文件
        fcount = json.load(f)                        #将json格式字符串解析为json类型
        self.count = fcount['pagecount']             #将页数信息赋值给count变量
        self.keywordscount = fcount['keywordscount'] #将关键词信息赋值给keywordscount变量
(2)记录数据

items.py

pagecount = scrapy.Field()                            #增加pagecount Item
keywordscount = scrapy.Field()                        #增加keywordscount Item

grouplinkspider.py

def getGroup(self, response):
    for substrlink in strlinks:
        item['pagecount'] = self.count                #记录页数信息
        item['keywordscount'] = self.keywordscount    #记录关键词信息
(3)保存数据

pipenlines.py

def __init__(self):
    self.pagecount = 0
    self.keywordscount = 0
def process_item(self, item, spider):
    self.pagecount = item['pagecount']
    self.keywordscount = item['keywordscount']
def close_spider(self, spider):
    count = {
        'pagecount': self.pagecount,
        'keywordscount': self.keywordscount
    }
    with open(',/count.json', 'w') as f:               #以写的方式打开文件
        json.dump(coun, f)                             #将数据保存到文件
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐