我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛
说明
趁着活动最后一天更新蹭蹭热度,看着中秋活动的文章大部分都是前端大佬们画动画,几乎看不到后端做了什么有意思的东西,现在我就来分享自己初学的Scrapy爬取一些网站的经历路程,接下来会使用Python语言,Scrapy框架获取V2ex网站的部分评论,还有一点Google的搜索结果页,V2ex是一个真实的程序员的讨论网站,氛围非常不错,和掘金一样经常会逛,评论真实,有血有肉。会有一些反扒机制,因为时间有限不去深入研究,目标也并不是什么全站的数据。
开始
安装一些后面可能会用到的包,不要惊奇会什么会用到pillow,jieba,wordcloud,一定要看到最后~
pip3 install scrapy
pip3 install pillow
pip3 install jieba
pip3 install numpy
pip3 install wordcloud
选择一个目录初始化项目:
scrapy startproject googlespider
然后会有提示让你进入文件夹创建爬虫,我们就跟着提示来 以此执行下面两条命令
cd googlespider
scrapy genspider v2ex google.com
接下来就会生成下面的目录
除了
中秋.png,get_word.py,juejin.jpg这三个文件是后面用到,其他都是上面命令生成的
前提设置
打开setting.py
ROBOTSTXT_OBEY = True 改为ROBOTSTXT_OBEY = False,这个是爬虫届的君子协议,大厂爬虫一般会遵循这个规则,我们这些小开发者一般不遵循,
修改并发设置
Scrapy框架是一个异步框架,效率那是相当的高,默认16,目前我们不研究反扒机制,只好设置成1咯,并发高了会IP被Ban掉。请求全部404,或者403。
CONCURRENT_REQUESTS = 1
打开默认的Headers
不能太明目张胆了把,直接告诉别人网站我是爬虫程序,委婉一点,掩饰一下,这是惯例了~
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
打开下载延迟
为了避开反扒只好退而求其次了~,频繁就会出问题。
AUTOTHROTTLE_START_DELAY = 5
正片开始
打开生成的v2ex.py,直接上代码,注释里说清楚
# -*- coding: utf-8 -*-
import scrapy
from scrapy.http.request import Request
from googlespider.items import GooglespiderItem
class V2exSpider(scrapy.Spider):
name = 'v2ex' #爬虫名字。启动时候会用到它
allowed_domains = ['google.com'] #限制这个爬虫程序爬取内容的域名,在复杂的爬虫中有很多情况会爬到其他站点去
start_urls = ['http://google.com/'] #自动生成的爬虫程序开始爬取的域名,一般不用它。
page_data = 10 #google 的翻页页数。相当于全局变量,后面的程序改变它使用它
#这里设置公用的headers是因为v2ex这个网站使用默认的headers请求状态直接403了,不让你访问,后面会补充这个header是如何生成的
headers = {
'authority': 'www.v2ex.com',
'cache-control': 'no-cache',
'sec-ch-ua': '"Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Linux"',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36',
'accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'no-cors',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'image',
'accept-language': 'zh-CN,zh;q=0.9',
'cookie': 'PB3_SESSION="2|1:0|10:1632320639|11:PB3_SESSION|36:djJleDo0Ny4yNTQuODQuMjA2Ojk5NTgyNDE3|af262dbef778709e4964d0dec124e60e267c03ac8e01bf98e702cb85b7fd0698"; V2EX_LANG=zhcn; _ga=GA1.2.2017158286.1632320642; _gid=GA1.2.1291035321.1632320642; A2="2|1:0|10:1632321211|2:A2|48:M2RjYzkzMWQtOTAwZi00YTA4LWEyNTctZmQ2NTdiMmY4YmMy|b75429367c29090d8e1b29d8f3c84a7c5e723005928c6d5267e4137e6bc02e91"; V2EX_REFERRER="2|1:0|10:1632321220|13:V2EX_REFERRER|8:VG9ieTIz|fbb0b7efd4f44fcd4298c61978cf3a395fd7111e54d8185344e5e8d286dff7f4"; V2EX_TAB="2|1:0|10:1632327619|8:V2EX_TAB|8:dGVjaA==|90bd1f7b91355e424c24b6192cbef107cde9747006042d46320b41b0b991173d"; _gat=1',
'Referer': 'https://www.v2ex.com/t/710481',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36',
'referer': 'https://www.v2ex.com/t/710481',
'Origin': 'https://www.v2ex.com',
'if-modified-since': 'Wed, 11 Aug 2021 00:32:57 GMT',
'Accept': '*/*',
'X-Requested-With': 'XMLHttpRequest',
'content-length': '0',
'content-type': 'text/plain',
'origin': 'https://www.v2ex.com',
'if-none-match': 'W/"55467a007c0429c0b04e98443edd5063d10f0b22"',
'pragma': 'no-cache',
'Content-Type': 'text/plain',
}
def start_requests(self):
# 这个函数是自己的写的,也是scrapy认可的,一开始会执行这个函数
#这个url是google的搜索页,site和inititle是google的高级搜索方法关键字,site指搜索结果只包含某个站点,intitle只搜索关键字只存与搜索结果网页的标题中
url = "https://www.google.com/search?q=site:v2ex.com/t+intitle:%E4%B8%AD%E7%A7%8B"
# yield这是Python的高级用法,迭代器,这里就是实现异步爬虫的关键要点,把这个url的请求解析工作交给了parse这个方法,当前函数可以继续向下执行,但是这里是没有下面的方法了,然后下面迭代器又有很多迭代器。就会出现很对的异步请求。
yield Request(url, callback=self.parse)
def parse(self, response):
# response对象是scrapy封装的对象,这里面有好多对象方法,例如下面的.selector.re就是使用正则提取网页关键内容的方法,我们提取google第一页的文章链接
url_list = response.selector.re("https://www.v2ex.com/t/[0-9]*")
print(url_list)
# 如果有文章链接就解析链接,把请求文章详情的任务用异步任务交给下一个方法去完成,然后翻页,直到google的结果页再也没有文章链接了,dont_filter就是不用最后开始限制只爬google的那个allowed_domains列表里的网站了。
if(len(url_list) > 0):
for i in url_list:
yield Request(url=i, callback=self.parse_detail, dont_filter=True,headers=self.headers)
yield Request(url="https://www.google.com/search?q=site:v2ex.com/t+intitle:%E4%B8%AD%E7%A7%8B&start="+str(self.page_data), callback=self.parse)
self.page_data += 10
def parse_detail(self, response):
# 这里使用xpath来解析v2ex文章内容。下面截图会补充
xpath_str = '//*[@class="reply_content"]/text()'
# 这里是收集爬取数据的管道,这个item管道会把数据交给下载器,下载器的代码内容下面接着说
item = GooglespiderItem()
word_list = response.xpath(xpath_str).getall()
if(len(word_list)>0):
for i in word_list:
item['word'] = i
#把爬取的内容交给管道,管道会把数据自动调度给下载器使用
yield item
核心功能代码展示完毕,接下来讲解其中要点
Headers伪装
Headers是突破反扒的第一个最有用的工具,最简单的就是模拟正常请求的Headers,这就要利用一个网站自动生成Python可用的Headers代码。 我们先正常使用浏览器访问网站,F12->NetWork->Doc,找到唯一的网页,右键copy->Copy as CURL
找到一个curl转python的在线网站,例如:tool.lu/curl
这样就得到了headers。
找到评论数据
下载google插件Xpath Helper 右键随意找到一个评论检查复制xpath,然后复制到Xpath Helper就会自动出结果,真是太快了,减少了好多调试的时间成本。
下载数据
格式化item,item是保存爬取到的数据的容器,主要作用是防止数据获取的时候拼写错误导致的未定义字段错误
打开items.py
在class GooglespiderItem(scrapy.Item):下加一行
word = scrapy.Field()
格式化item后要编写下载中间件,这里很简单就不细说了:
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import csv
import codecs
import re,os
class GooglespiderPipeline(object):
def process_item(self, item, spider):
return item
class CsvspiderPipeline(object):
def __init__(self):
# 构造方法打开一个csv文件,不存在就创建
self.file = codecs.open('word.csv', 'w', encoding='utf_8_sig')
def process_item(self, item, spider):
fieldnames = ['word']
w = csv.DictWriter(self.file, fieldnames=fieldnames)
print(item) #这里是一个字典
#写入文件
w.writerow(item)
return item
def close_spider(self, spider):
# 关闭文件
self.file.close()
然后打开setting.py里面的设置,290是中间件优先级,复杂的项目会有很多中间件,这里只有一个随便设咯
ITEM_PIPELINES = {
'googlespider.pipelines.CsvspiderPipeline': 290,
}
运行
在项目根目录(就是在scrapy.cfg同目录下)运行下列命令:
scrapy crawl v2ex
然后等待几分钟就爬完了就会生成一个word.csv文件在项目目录下
处理数据
使用结巴分词,wordcloud,PIL等模块做一个简单的词云图片,直接上代码吧,这里没有深入研究,找了些资料都是调API,哈哈,人生苦短,我用Python~
import csv
import jieba.analyse as analyse
from PIL import Image
import matplotlib.pyplot as plt
import wordcloud
import numpy as np
all_text=''
# 读取csv,把所有的句子连成一句话
with open('./word.csv')as f:
f_csv = csv.reader(f)
for row in f_csv:
print(row[0])
all_text+=row[0]+';'
image1 = Image.open('./juejin.jpg') # 打开一个背景图作素材
tags = analyse.extract_tags(all_text, topK=100) #著名的结巴分词,挑选出最常见的100个次
text = ' '.join(tags) #把100歌词的列表改为空格间隔的一行字符串给云词这个库使用
MASK = np.array(image1) #词云的遮照为nampy数组
# 下面就是调WordCloud的api身成一张图,可以设置宽高,词的数量,背景图什么的,注意字体文件需要根据自己的系统找,我的是ubuntu的。
WC = wordcloud.WordCloud(font_path= "/usr/share/fonts/truetype/arphic/ukai.ttc",max_words=2000,mask = MASK,height= 400,width=400,background_color='white',repeat=False,mode='RGBA') #设置词云图对象属性
#生成图片
con = WC.generate(text)
# 展示图片
plt.imshow(con)
# 关闭坐标轴,plt这是一个科学计算相关的库
plt.axis("off")
plt.show()
print(tags)
背景图:
成果展示:
总结
所有代码在GitHub,大家觉得学到了一些东西了可以看看参考一下:koala9527/v2ex-google-scrapy-spider
结果没有什么出乎意料的词语,例如:'快乐', '中秋节', '中秋', '月饼'。也有一些小众的词语比较有意思,例如:'女朋友','学习','睡觉',哈哈。
爬虫技术难点在于反爬,最开始请求频率高了Google和V2ex请求响应都会直接404或者403,只能短时间获取一小部分数据,如果想要大量的数据,只能上动态IP,动态Headers,甚至一行行研究JS代码。
最后感谢大家欣赏,看到最后的都是大佬,欢迎大佬查漏补缺,指出错误的地方~