pyrthon 简单爬虫实现
简单爬虫的通用步骤
BY ZKEEER 2017-09-03 2 COMMENTS
本文首发:ZKeeer’s Blog——简单爬虫的通用步骤
代码基于 python3.5
多图预警,长文预警知识点很多,适合小白,大神绕路
1.获取数据
爬虫,就是要千方百计地装成浏览器从网站骗数据。——我说的
1.1从requests.get()说起
最开始一个简单的爬虫就是调用python的requests模块,使用get函数。(为了不祸害别人网站,我以自己的网站为例)
import requests
url_response = requests.get("http://zkeeer.space")
print(url_response.status_code, url_response.text)
这里get函数从给出的URL获取数据,打印出状态码和获取的内容看看。
200 <!DOCTYPE html>
<html lang="zh-CN">
<head>......
状态码200,说明平稳落地。后面是获取到的网页。
这里要说明一点,url_response.text 和 url_response.content的区别:
.text返回的是Unicode类型,.content返回的是bytes型也就是传说的二进制的数据。当需要的数据是文本时,最好用.text,当你需要下载图片时,要用.content
上面是.text返回的值,下面打印出来.content的值让大家看看。
200 b'<!DOCTYPE html>\r\n<html lang="zh-CN">\r\n <head>
看到前面的小b以及后面赤裸裸的\r\n了么?
我的博客挺简单没有那么大访问量,也不需要限制访问量,也不需要严查你的IP,UserAgent,Cookie等。当你需要大量,高频次访问,而且访问的还是淘宝这样的商业网站,这时候你就需要伪装了,不能只是赤裸裸的用个get加个url,就向网站大喊:“我!浏览器!给数据!” 也就我的博客这么好心给你,淘宝早就会“淘宝不想理你,并向你扔了个大创可贴”。
1.2学会使用火狐浏览器开发者工具
如何伪装一个浏览器?
学习当然都是从模仿开始——也是我说的!
这里使用的是火狐浏览器开发者工具,别听这么高大上,其实就是打开火狐浏览器按F12!
第一步输入网址进入我的博客,http://zkeeer.space 然后按F12,找到网络这一栏。它会提示你重新载入,那就按一下F5,刷新一下。
注意以下几栏。然后找到并点开我们需要的,也就是第一个
右侧会出来对应的详细信息。包括:消息头,Cookie,参数,响应,耗时,堆栈跟踪。
首先requests.get(url, params=None, **kwargs),下面的顺序按照参数顺序,一一来。
1.3requests.get()参数一:url
消息头这一栏给出的请求方法是GET,即请求时使用requests.get(),如果这里是POST,对应使用requests.post()。get函数的url,即请求头的Host,这里是“zkeeer.space”
1.4requests.get()参数二:params
get(url, params=None, **kwargs)中params,是构成网址中一些参数,网址链接“?”后面的那些参数。举个栗子:我的一篇文章链接是http://zkeeer.space/?p=383 那么后面p=383就是get 的参数(当然你也可以直接访问http://zkeeer.space/?p=383,而不需要参数)。
需要把这里列举的参数都写到params里面,哪怕是该参数没有值。
那么一开头我们那几行代码就应该这么写了。
import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
url_response = requests.get(url=tar_url, params=param)
print(url_response.status_code, url_response.text)
这样获取到的页面就是“zkeeer.space/?p=383”对应的文章了。
1.5requests.get()参数三:headers
可能会有疑问,get(url, params=None, **kwargs)并没有headers这个参数啊。这个包含在**kwargs里面,同样还有另一常用的proxies,待会儿会说到。
headers应该写什么呢?下图所示的消息头中的请求头即是这里的headers参数。
跟参数一样,需要把请求头的所有信息写入headers(如果网站不查cookie的话,cookie没必要写)。如果对这些参数不了解,可以点击后面对应的详细了解,介绍的很详细。
上面的几行代码又要进化了。
import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, headers=header)
print(url_response.status_code, url_response.text)
这样一来就比较完备了。
但是这样情况下,高频次访问对服务器造成压力了,可能会分析哪个UserAgent访问次数最多,发现是你在狂刷人家网站,这时候就有可能给你禁了这个UserAgent,这时候你需要更多的UserAgent随机挑选进行访问。
我在上篇文章《获取爬虫所需要的代理IP》中的代码中就有收集的UserAgent,见Github 。
可以使用random.choice()随便挑嘛。
1.6requests.get()参数四:proxies
上面提到了,这个参数是代理,对,是代理。当你使用随机UserAgent的人家没法封了。就会查你IP,发现这个IP刷爆了网站,直接就封了。这时候你要使用代理IP。
上面的代码又进化了呢!
import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
proxy = {"http": "http://{}:{}".format("221.8.186.249", "80"),
"https": "https://{}:{}".format("221.8.186.249", "80")} #代理IP
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, proxies=proxy, headers=header)
print(url_response.status_code, url_response.text)
问题又来了,长时间使用同一个代理IP,也会被封啊,我要获取大量代理IP,最好还是匿名或者高匿。这些代理IP从哪来的?我该怎么获取?上篇文章我写了如果获取大量代理IP以及使用:获取爬虫所需要的代理IP
如果需要高质量IP可以从代理网站或者淘宝买,大量,不贵。
1.7总结
至此,基本的获取网页已经差不多了,大多数网站你都可以畅行。尽管,也不能暴力访问一个网站,要有公德心嘛,我写爬虫还sleep(0.5)呢。
火狐浏览器的开发者工具很好用,希望大家能发挥其作用。
2.提取数据
获取完网页接下来应该提取数据了。获取网页的数据,我想提取网页中特定的文字,或者是数据,或者是图片,这就是网页主要提取的吧。
2.1提取文字
先说提取文字,强烈推荐正则表达式,太强大了。简直就是加特林哒哒哒哒冒蓝火的那种;正则在手,天下我有;当然我的水平仅限于能用,就不出来献丑了。大家按照网上的教程来就可以。当然你也可以用beautifulsoup。正则表达式和beautifulsoup这两种效率比较高。
2.2提取图片
以我博客中《获取爬虫所需要的代理IP》文章为例,提取其中的图片。提取网页中的图片,我们可以用提取文字的方式,用正则表达式获取图片链接。这时候,又用的get函数。跟获取网页一样,获取图片。记得前面说过的text和content的区别。这里要使用后者。那么如何保存一张图片呢?看下面的代码示例。
打开火狐浏览器开发者工具。这次使用的是查看器,而不是网络。一层层找到上面文章中的图片所在位置及相关链接。
找到了<img>标签,这时可以右键查看网页源代码,查找一下看有多少是跟你目标相似的,看看如何区分
我找到了10项,但只有一项是我需要的,于是我把正则式写成了
<img class=\"[^\"]+\" src=\"([^\"]+)\"
这时候可以用 站长工具-正则表达式在线测试 测试下自己写的正则表达式是否正确
成功了!代码如下:
import re import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, headers=header) img_url = re.findall(r"<img class=\"[^\"]+\" src=\"([^\"]+)\"", url_response.text)
print(img_url[0])
输出结果:
http://zkeeer.space/wp-content/uploads/2017/08/逻辑图-1024x576.png
这样有了图片的链接,我们就可以使用链接获取并保存图片了。
一定要注意使用F12,并仔细查看请求头和参数!
一定要注意使用F12,并仔细查看请求头和参数!
一定要注意使用F12,并仔细查看请求头和参数!
import re import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, headers=header) img_header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/?p=383",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
# 获取图片链接
img_url = re.findall(r"<img class=\"[^\"]+\" src=\"([^\"]+)\"", url_response.text)[0]
# 从图片链接中提取图片名
img_name = re.findall(r"([^/]+.png)", img_url)[0]
# 请求
url_response = requests.get(url=img_url, headers=img_header)
# 保存图片
with open(img_name, "wb") as fw:
fw.write(url_response.content)
运行程序查看得到的图片(我的vps配置特别低,速度特慢,轻喷。给Vultr的VPS打个广告,有优惠!)
好了,到这儿可以获取基本的网页信息了。而且高频次访问基本上不会被封。
你以为到这就结束了吗?
2.3提取动态加载的数据
在获取有些重要数据的时候。这些数据是动态加载的。这也是反爬虫的一种手段,下面我有提及反爬虫。
当我只获取这个网页的时候,根本不会显示这些数据。这个时候又要让你的爬虫装一次浏览器。当然不能说:我!浏览器!给数据! 这个时候你不光要卖萌,还要回答问题。快看快看,我是浏览器,我是可爱的浏览器,快给我数据啊。
这时候人家就问你,你是从哪儿(Refer)找到我的?你的鸡毛信(ID)呢?你从袖筒里排出几个参数,浏览器一看,哎呀,大兄弟,真的是你,快给你数据。这时候不要吭声,趁他不注意,拿了数据赶紧跑。
获取方式跟上面获取网页一样。同样是使用火狐。以淘宝米6商品评论为例,我随便找的,别说我软广:
点击评论,然后看到网络这一栏,动态加载的内容就出来了,从域名里我找到了rate.taobao.com,对应左边两个文件,一个是detailCommon.htm 另一个是freeRateList.htm;我查看了下,分别对应评论中的大家印象和详细评论。查看哪儿?查看对应的响应内容。
这里就以“大家印象”为例:按照1.获取数据的步骤,获取到了如下结果:
import re
import requests tar_url = "https://rate.taobao.com/detailCommon.htm"
# 商品链接
refer = "https://item.taobao.com/item.htm?spm=a230r.1.14.119.76bf523Zih6Ob&id=548765652209&ns=1&abbucket=12"
# 从商品链接中提取商品ID
NumId = re.findall(r"id=(\d+)\&", refer)
# 参数
param = {"auctionNumId": NumId,
"userNumId": "43440508",
"callback": "json_tbc_rate_summary"}
# 头部信息
header = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, compress',
'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,ru;q=0.4',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': "1",
'Referer': refer,
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0"
} try:
url_content = requests.get(url=tar_url, params=param, headers=header)
print("url_content: ", url_content.text)
except BaseException as e:
pass
结果:
json_tbc_rate_summary({"watershed":100,"isShowDefaultSort":true,"askAroundDisabled":false,"sellerRefundCount":6,"skuSelected":true,"data":{"newSearch":false,"count":{"total":4689,"tryReport":0,"goodFull":4622,"additional":243,"correspond":0,"normal":30,"hascontent":0,"good":4622,"pic":588,"bad":37,"totalFull":4689},"spuRatting":[],"correspond":"4.8","impress":[{"title":"手机不错","count":477,"value":1,"attribute":"620-11","scm":""},{"title":"手机是正品","count":243,"value":1,"attribute":"1020-11","scm":""},{"title":"系统很强大","count":214,"value":1,"attribute":"921-11","scm":""},{"title":"态度不错","count":144,"value":1,"attribute":"10120-11","scm":""},{"title":"款式好看","count":115,"value":1,"attribute":"121-11","scm":""},{"title":"快递不错","count":108,"value":1,"attribute":"420-11","scm":""},{"title":"手感很好","count":90,"value":1,"attribute":"721-11","scm":""},{"title":"性价比很高","count":85,"value":1,"attribute":"20520-11","scm":""},{"title":"性能一般","count":39,"value":-1,"attribute":"921-13","scm":""},{"title":"配件一般","count":35,"value":-1,"attribute":"621-13","scm":""}],"attribute":[{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]},{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]},{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]},{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]}]},"skuFull":false,"showPicRadio":true,"isRefundUser":true})
最外层是个json_tbc_rate_summary()回调函数,它的参数是个词典,我们词典拿出来,放到python中看起来就比较方便了。
处理结果:
2.4总结
这样一来,随便你怎么造,基本的数据你都能获取到了,动态内容你也有了。正则式也有了,随便搞。看到这里,基本可以爬取一些网页了,下面的内容都是在吹牛,不看也罢。
尽管你都能获取到了,但是效率呢?下面说说怎样高效抓取。
3.高效抓取数据(多线程/多进程/分布式爬虫)
从这儿以后的内容我了解的不深,大部分内容我也在学,权当一起聊聊,说错的地方请及时指出;我尽力说说,重在大家自己的修行。多看书,多学习。
3.1多线程爬虫
以上篇文章《获取爬虫所需要的代理IP》中验证IP可用性来说,当数据库和获取到的数据中有多个IP,假设有1000IP,如果是串行的话,一个个IP去验证,效率非常低了,更何况遇到不能用的还会超时。这里使用多线程,明显提高了效率。
不谈多线程的原理/好处,只说说怎么使用。因为我不会!你来打我呀!
上篇文章中,验证IP时,将IP全部读入列表。多个线程从列表中获取、验证,然后将可用的IP放入词典(主要为了去重)中。这里只是给大家一个思路,还比如,你设置一个或者多个线程专门抓取网页上的链接然后放在列表中,然后多个线程从列表中取/访问链接,这就涉及到生产者消费者问题了。不多说了,大家应该多学学这里,以后用得着。
线程数量多少依据电脑性能来定。
这里使用的模块是threadings,具体怎么用,网上的例子比我说得好,万一我跟他想到一块儿去了,说的一样好了,那不就来指控我抄袭?参考廖雪峰老师的教程:多线程
代码:Github 多线程的使用在GetIP.py文件中。
逻辑图:
这虽然不是串行,效率提高了好多,但这还不够,我要做你近旁的一株木棉……跑题了,这还不够,并发效率虽然提高了,但是不能有效的利用CPU的多核,经常是1核有难,7核围观。蛋疼的GIL啊。(大家有兴趣可以百度“python 多线程能利用多核吗”)
接下来你需要的是多进程爬虫。多个CPU一起建设社会主义,大步迈入小康社会!
3.2多进程爬虫
如果单单是多进程爬虫,执行效率会提高,再加上协程的话,效率会有明显提高。逻辑结构同多线程类似,只不过进程之间通信要使用Queue或者Pipes。
多进程的使用参考廖雪峰老师的教程
大体结构如下图
我把那个获取代理IP改写成多进程版本再发出来。
3.3分布式爬虫
当网络带宽、端口资源、IP资源、存储资源满足不了爬虫时,应当适用分布式爬虫,最基本的分布式爬虫结构如下图:
每一个从机上都可以再使用多进程。
我还没写过分布式。推荐一个Github项目,简单的分布式爬虫,爬取知乎用户:ZhiHu Spider based on Python
3.4总结
提升效率的方法:串行->并发->并行->分布式。
提升效率了也得注意公德,别给人家服务造成太大压力,适当的sleep。
4.持续抓取数据(增量式爬虫)
是 指 对 已 下 载 网 页 采 取 增 量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。 和周期性爬行和刷新页面的网络爬虫相比,增量式爬虫只会在需要的时候爬行新产生或发生更新的页面 ,并不重新下载没有发生变化的页面,可有效减少数据下载量,及时更新已爬行的网页,减小时间和空间上的耗费,但是增加了爬行算法的复杂度和实现难度。——百度百科
举例,拿最热的《战狼2》来说,我要实时获取最新的影评,进行一系列操作,但是不抓已经保存的评论,只检测最新的评论然后获取/处理。知乎上的大神们提到增量式爬虫重点在怎么判断是否抓取过,从而避免重复抓取;再一个就是数据去重。
推荐Github项目:byrSpider
5.爬虫和反爬虫和反反爬虫
爬虫(Spider),反爬虫(Anti-Spider),反反爬虫(Anti-Anti-Spider),是一场精彩绝伦的战争,你方唱罢我登场。
5.1动态加载
关于动态加载,一种是前面在提到的提取动态内容,方法差不多。另外可以使用selenium/phantomjs提取啊。
因为爬虫不会执行js代码进行渲染,有的可能会使用js代码与服务器通信,回传一定的数据,若是爬虫,这段js代码便不会执行,可以封IP了;或者给你假数据,投毒。
还遇到过,抓取代理IP时,某个网站需要调用API来checkuser,只需要调用,哪怕check失败了;如果不调用的话,就得不到数据。这就跟上面差不多。
这时候用必要使用selenium/phantomjs了,但是会大大降低效率。
还想到的其他反爬虫策略也有改动页面结构。
5.2验证码
当网站判断你是个爬虫时,经常会跳出几个验证码来打断你……的腿。处理验证码,常用的方法有:
1.频率不高,数据量不大时,可以手动输入
2.频率高,数据量大,考虑接入打码平台。你们知道吗,这叫服务,得花钱!
3.当以上行不通时,自己写程序识别验证码啊。我会说我本科毕设做的就是基于深度神经网络的验证码识别吗?会!由于做的不成熟,就不拿出来现眼了。验证码识别在知乎和GitHub上都有相关的项目,可以参考做一下。
4.还有哪些非传统的验证码,例如12306的选择图片、滑块验证、记录鼠标轨迹和点击等等。12306那个可以调用谷歌或者百度的识图API,滑块和鼠标轨迹之类的得借助phantomjs。等我学会了我再讲出来。这里会耗费精力很大。多多sleep会在很大程度上避免验证码。
5.3登录
还在学习这个,懂得不多,不吹牛皮,推荐Github项目:模拟登录一些知名的网站,为了方便爬取需要登录的网站
5.4cookie
大多数不是真心想要反爬虫的网站,或者保护重要数据,不会查你的cookie。推荐几篇关于cookie的文章,别的以后想起来再补充。
Find Hao——python爬虫学习(四)获取cookie
州的先生——Python爬虫实战入门四:使用Cookie模拟登录——获取电子书下载链接
5.5总结
其实绕过这些反爬虫手段才是王道,正面刚得费多少精力。间隔时间大一点儿,并发量小一点儿,不给人家服务器造成压力,人家才不会盯上你。出来混都不容易,谁还不是个宝宝。
6.推荐
1.有能力的去阅读Scrapy源码
2.推荐书目:《HTTP权威指南》《用python写网络爬虫》,前端方面我是小白,就知道本《CSS权威指南》求大家推荐基础的前端书籍。
3.python相关的:路人甲大神的python总结,还有知乎上好多python大牛,多多关注他们
4.我这儿还有一些python的电子书,包括网上的和我自己买的,都贡献出来
链接:http://pan.baidu.com/s/1skEQeTZ 密码:gi91
5.正则表达式相关的,我倒是经常看见这个大哥(国服第一奇葩辅助)回答
This article was written by ZKeeer
文章导航
2 THOUGHTS ON “简单爬虫的通用步骤”
- 小咸鱼说道:
博主,你的VPS配置是多少的?多少的配置可以满足日常爬虫需要?
- ZKeeer说道:
我的博客vps是vultr家的,1核512M,挺便宜的,推荐一下https://www.vultr.com/?ref=7170172
如果要单纯用vps部署爬虫,要看爬虫的设计来决定vps配置了。
简单爬虫的通用步骤
BY ZKEEER 2017-09-03 2 COMMENTS
本文首发:ZKeeer’s Blog——简单爬虫的通用步骤
代码基于 python3.5
多图预警,长文预警知识点很多,适合小白,大神绕路
1.获取数据
爬虫,就是要千方百计地装成浏览器从网站骗数据。——我说的
1.1从requests.get()说起
最开始一个简单的爬虫就是调用python的requests模块,使用get函数。(为了不祸害别人网站,我以自己的网站为例)
import requests
url_response = requests.get("http://zkeeer.space")
print(url_response.status_code, url_response.text)
这里get函数从给出的URL获取数据,打印出状态码和获取的内容看看。
200 <!DOCTYPE html>
<html lang="zh-CN">
<head>......
状态码200,说明平稳落地。后面是获取到的网页。
这里要说明一点,url_response.text 和 url_response.content的区别:
.text返回的是Unicode类型,.content返回的是bytes型也就是传说的二进制的数据。当需要的数据是文本时,最好用.text,当你需要下载图片时,要用.content
上面是.text返回的值,下面打印出来.content的值让大家看看。
200 b'<!DOCTYPE html>\r\n<html lang="zh-CN">\r\n <head>
看到前面的小b以及后面赤裸裸的\r\n了么?
我的博客挺简单没有那么大访问量,也不需要限制访问量,也不需要严查你的IP,UserAgent,Cookie等。当你需要大量,高频次访问,而且访问的还是淘宝这样的商业网站,这时候你就需要伪装了,不能只是赤裸裸的用个get加个url,就向网站大喊:“我!浏览器!给数据!” 也就我的博客这么好心给你,淘宝早就会“淘宝不想理你,并向你扔了个大创可贴”。
1.2学会使用火狐浏览器开发者工具
如何伪装一个浏览器?
学习当然都是从模仿开始——也是我说的!
这里使用的是火狐浏览器开发者工具,别听这么高大上,其实就是打开火狐浏览器按F12!
第一步输入网址进入我的博客,http://zkeeer.space 然后按F12,找到网络这一栏。它会提示你重新载入,那就按一下F5,刷新一下。
注意以下几栏。然后找到并点开我们需要的,也就是第一个
右侧会出来对应的详细信息。包括:消息头,Cookie,参数,响应,耗时,堆栈跟踪。
首先requests.get(url, params=None, **kwargs),下面的顺序按照参数顺序,一一来。
1.3requests.get()参数一:url
消息头这一栏给出的请求方法是GET,即请求时使用requests.get(),如果这里是POST,对应使用requests.post()。get函数的url,即请求头的Host,这里是“zkeeer.space”
1.4requests.get()参数二:params
get(url, params=None, **kwargs)中params,是构成网址中一些参数,网址链接“?”后面的那些参数。举个栗子:我的一篇文章链接是http://zkeeer.space/?p=383 那么后面p=383就是get 的参数(当然你也可以直接访问http://zkeeer.space/?p=383,而不需要参数)。
需要把这里列举的参数都写到params里面,哪怕是该参数没有值。
那么一开头我们那几行代码就应该这么写了。
import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
url_response = requests.get(url=tar_url, params=param)
print(url_response.status_code, url_response.text)
这样获取到的页面就是“zkeeer.space/?p=383”对应的文章了。
1.5requests.get()参数三:headers
可能会有疑问,get(url, params=None, **kwargs)并没有headers这个参数啊。这个包含在**kwargs里面,同样还有另一常用的proxies,待会儿会说到。
headers应该写什么呢?下图所示的消息头中的请求头即是这里的headers参数。
跟参数一样,需要把请求头的所有信息写入headers(如果网站不查cookie的话,cookie没必要写)。如果对这些参数不了解,可以点击后面对应的详细了解,介绍的很详细。
上面的几行代码又要进化了。
import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, headers=header)
print(url_response.status_code, url_response.text)
这样一来就比较完备了。
但是这样情况下,高频次访问对服务器造成压力了,可能会分析哪个UserAgent访问次数最多,发现是你在狂刷人家网站,这时候就有可能给你禁了这个UserAgent,这时候你需要更多的UserAgent随机挑选进行访问。
我在上篇文章《获取爬虫所需要的代理IP》中的代码中就有收集的UserAgent,见Github 。
可以使用random.choice()随便挑嘛。
1.6requests.get()参数四:proxies
上面提到了,这个参数是代理,对,是代理。当你使用随机UserAgent的人家没法封了。就会查你IP,发现这个IP刷爆了网站,直接就封了。这时候你要使用代理IP。
上面的代码又进化了呢!
import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
proxy = {"http": "http://{}:{}".format("221.8.186.249", "80"),
"https": "https://{}:{}".format("221.8.186.249", "80")} #代理IP
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, proxies=proxy, headers=header)
print(url_response.status_code, url_response.text)
问题又来了,长时间使用同一个代理IP,也会被封啊,我要获取大量代理IP,最好还是匿名或者高匿。这些代理IP从哪来的?我该怎么获取?上篇文章我写了如果获取大量代理IP以及使用:获取爬虫所需要的代理IP
如果需要高质量IP可以从代理网站或者淘宝买,大量,不贵。
1.7总结
至此,基本的获取网页已经差不多了,大多数网站你都可以畅行。尽管,也不能暴力访问一个网站,要有公德心嘛,我写爬虫还sleep(0.5)呢。
火狐浏览器的开发者工具很好用,希望大家能发挥其作用。
2.提取数据
获取完网页接下来应该提取数据了。获取网页的数据,我想提取网页中特定的文字,或者是数据,或者是图片,这就是网页主要提取的吧。
2.1提取文字
先说提取文字,强烈推荐正则表达式,太强大了。简直就是加特林哒哒哒哒冒蓝火的那种;正则在手,天下我有;当然我的水平仅限于能用,就不出来献丑了。大家按照网上的教程来就可以。当然你也可以用beautifulsoup。正则表达式和beautifulsoup这两种效率比较高。
2.2提取图片
以我博客中《获取爬虫所需要的代理IP》文章为例,提取其中的图片。提取网页中的图片,我们可以用提取文字的方式,用正则表达式获取图片链接。这时候,又用的get函数。跟获取网页一样,获取图片。记得前面说过的text和content的区别。这里要使用后者。那么如何保存一张图片呢?看下面的代码示例。
打开火狐浏览器开发者工具。这次使用的是查看器,而不是网络。一层层找到上面文章中的图片所在位置及相关链接。
找到了<img>标签,这时可以右键查看网页源代码,查找一下看有多少是跟你目标相似的,看看如何区分
我找到了10项,但只有一项是我需要的,于是我把正则式写成了
<img class=\"[^\"]+\" src=\"([^\"]+)\"
这时候可以用 站长工具-正则表达式在线测试 测试下自己写的正则表达式是否正确
成功了!代码如下:
import re import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, headers=header) img_url = re.findall(r"<img class=\"[^\"]+\" src=\"([^\"]+)\"", url_response.text)
print(img_url[0])
输出结果:
http://zkeeer.space/wp-content/uploads/2017/08/逻辑图-1024x576.png
这样有了图片的链接,我们就可以使用链接获取并保存图片了。
一定要注意使用F12,并仔细查看请求头和参数!
一定要注意使用F12,并仔细查看请求头和参数!
一定要注意使用F12,并仔细查看请求头和参数!
import re import requests tar_url = "http://zkeeer.space" # 目标网页
param = {"p": 383} # 请求头的参数
header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
url_response = requests.get(url=tar_url, params=param, headers=header) img_header = { # 请求头部
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://zkeeer.space/?p=383",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
# 获取图片链接
img_url = re.findall(r"<img class=\"[^\"]+\" src=\"([^\"]+)\"", url_response.text)[0]
# 从图片链接中提取图片名
img_name = re.findall(r"([^/]+.png)", img_url)[0]
# 请求
url_response = requests.get(url=img_url, headers=img_header)
# 保存图片
with open(img_name, "wb") as fw:
fw.write(url_response.content)
运行程序查看得到的图片(我的vps配置特别低,速度特慢,轻喷。给Vultr的VPS打个广告,有优惠!)
好了,到这儿可以获取基本的网页信息了。而且高频次访问基本上不会被封。
你以为到这就结束了吗?
2.3提取动态加载的数据
在获取有些重要数据的时候。这些数据是动态加载的。这也是反爬虫的一种手段,下面我有提及反爬虫。
当我只获取这个网页的时候,根本不会显示这些数据。这个时候又要让你的爬虫装一次浏览器。当然不能说:我!浏览器!给数据! 这个时候你不光要卖萌,还要回答问题。快看快看,我是浏览器,我是可爱的浏览器,快给我数据啊。
这时候人家就问你,你是从哪儿(Refer)找到我的?你的鸡毛信(ID)呢?你从袖筒里排出几个参数,浏览器一看,哎呀,大兄弟,真的是你,快给你数据。这时候不要吭声,趁他不注意,拿了数据赶紧跑。
获取方式跟上面获取网页一样。同样是使用火狐。以淘宝米6商品评论为例,我随便找的,别说我软广:
点击评论,然后看到网络这一栏,动态加载的内容就出来了,从域名里我找到了rate.taobao.com,对应左边两个文件,一个是detailCommon.htm 另一个是freeRateList.htm;我查看了下,分别对应评论中的大家印象和详细评论。查看哪儿?查看对应的响应内容。
这里就以“大家印象”为例:按照1.获取数据的步骤,获取到了如下结果:
import re
import requests tar_url = "https://rate.taobao.com/detailCommon.htm"
# 商品链接
refer = "https://item.taobao.com/item.htm?spm=a230r.1.14.119.76bf523Zih6Ob&id=548765652209&ns=1&abbucket=12"
# 从商品链接中提取商品ID
NumId = re.findall(r"id=(\d+)\&", refer)
# 参数
param = {"auctionNumId": NumId,
"userNumId": "43440508",
"callback": "json_tbc_rate_summary"}
# 头部信息
header = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, compress',
'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,ru;q=0.4',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': "1",
'Referer': refer,
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0"
} try:
url_content = requests.get(url=tar_url, params=param, headers=header)
print("url_content: ", url_content.text)
except BaseException as e:
pass
结果:
json_tbc_rate_summary({"watershed":100,"isShowDefaultSort":true,"askAroundDisabled":false,"sellerRefundCount":6,"skuSelected":true,"data":{"newSearch":false,"count":{"total":4689,"tryReport":0,"goodFull":4622,"additional":243,"correspond":0,"normal":30,"hascontent":0,"good":4622,"pic":588,"bad":37,"totalFull":4689},"spuRatting":[],"correspond":"4.8","impress":[{"title":"手机不错","count":477,"value":1,"attribute":"620-11","scm":""},{"title":"手机是正品","count":243,"value":1,"attribute":"1020-11","scm":""},{"title":"系统很强大","count":214,"value":1,"attribute":"921-11","scm":""},{"title":"态度不错","count":144,"value":1,"attribute":"10120-11","scm":""},{"title":"款式好看","count":115,"value":1,"attribute":"121-11","scm":""},{"title":"快递不错","count":108,"value":1,"attribute":"420-11","scm":""},{"title":"手感很好","count":90,"value":1,"attribute":"721-11","scm":""},{"title":"性价比很高","count":85,"value":1,"attribute":"20520-11","scm":""},{"title":"性能一般","count":39,"value":-1,"attribute":"921-13","scm":""},{"title":"配件一般","count":35,"value":-1,"attribute":"621-13","scm":""}],"attribute":[{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]},{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]},{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]},{"name":"版本类型","options":[{"name":"中国大陆","value":"中国大陆"}]}]},"skuFull":false,"showPicRadio":true,"isRefundUser":true})
最外层是个json_tbc_rate_summary()回调函数,它的参数是个词典,我们词典拿出来,放到python中看起来就比较方便了。
处理结果:
2.4总结
这样一来,随便你怎么造,基本的数据你都能获取到了,动态内容你也有了。正则式也有了,随便搞。看到这里,基本可以爬取一些网页了,下面的内容都是在吹牛,不看也罢。
尽管你都能获取到了,但是效率呢?下面说说怎样高效抓取。
3.高效抓取数据(多线程/多进程/分布式爬虫)
从这儿以后的内容我了解的不深,大部分内容我也在学,权当一起聊聊,说错的地方请及时指出;我尽力说说,重在大家自己的修行。多看书,多学习。
3.1多线程爬虫
以上篇文章《获取爬虫所需要的代理IP》中验证IP可用性来说,当数据库和获取到的数据中有多个IP,假设有1000IP,如果是串行的话,一个个IP去验证,效率非常低了,更何况遇到不能用的还会超时。这里使用多线程,明显提高了效率。
不谈多线程的原理/好处,只说说怎么使用。因为我不会!你来打我呀!
上篇文章中,验证IP时,将IP全部读入列表。多个线程从列表中获取、验证,然后将可用的IP放入词典(主要为了去重)中。这里只是给大家一个思路,还比如,你设置一个或者多个线程专门抓取网页上的链接然后放在列表中,然后多个线程从列表中取/访问链接,这就涉及到生产者消费者问题了。不多说了,大家应该多学学这里,以后用得着。
线程数量多少依据电脑性能来定。
这里使用的模块是threadings,具体怎么用,网上的例子比我说得好,万一我跟他想到一块儿去了,说的一样好了,那不就来指控我抄袭?参考廖雪峰老师的教程:多线程
代码:Github 多线程的使用在GetIP.py文件中。
逻辑图:
这虽然不是串行,效率提高了好多,但这还不够,我要做你近旁的一株木棉……跑题了,这还不够,并发效率虽然提高了,但是不能有效的利用CPU的多核,经常是1核有难,7核围观。蛋疼的GIL啊。(大家有兴趣可以百度“python 多线程能利用多核吗”)
接下来你需要的是多进程爬虫。多个CPU一起建设社会主义,大步迈入小康社会!
3.2多进程爬虫
如果单单是多进程爬虫,执行效率会提高,再加上协程的话,效率会有明显提高。逻辑结构同多线程类似,只不过进程之间通信要使用Queue或者Pipes。
多进程的使用参考廖雪峰老师的教程
大体结构如下图
我把那个获取代理IP改写成多进程版本再发出来。
3.3分布式爬虫
当网络带宽、端口资源、IP资源、存储资源满足不了爬虫时,应当适用分布式爬虫,最基本的分布式爬虫结构如下图:
每一个从机上都可以再使用多进程。
我还没写过分布式。推荐一个Github项目,简单的分布式爬虫,爬取知乎用户:ZhiHu Spider based on Python
3.4总结
提升效率的方法:串行->并发->并行->分布式。
提升效率了也得注意公德,别给人家服务造成太大压力,适当的sleep。
4.持续抓取数据(增量式爬虫)
是 指 对 已 下 载 网 页 采 取 增 量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。 和周期性爬行和刷新页面的网络爬虫相比,增量式爬虫只会在需要的时候爬行新产生或发生更新的页面 ,并不重新下载没有发生变化的页面,可有效减少数据下载量,及时更新已爬行的网页,减小时间和空间上的耗费,但是增加了爬行算法的复杂度和实现难度。——百度百科
举例,拿最热的《战狼2》来说,我要实时获取最新的影评,进行一系列操作,但是不抓已经保存的评论,只检测最新的评论然后获取/处理。知乎上的大神们提到增量式爬虫重点在怎么判断是否抓取过,从而避免重复抓取;再一个就是数据去重。
推荐Github项目:byrSpider
5.爬虫和反爬虫和反反爬虫
爬虫(Spider),反爬虫(Anti-Spider),反反爬虫(Anti-Anti-Spider),是一场精彩绝伦的战争,你方唱罢我登场。
5.1动态加载
关于动态加载,一种是前面在提到的提取动态内容,方法差不多。另外可以使用selenium/phantomjs提取啊。
因为爬虫不会执行js代码进行渲染,有的可能会使用js代码与服务器通信,回传一定的数据,若是爬虫,这段js代码便不会执行,可以封IP了;或者给你假数据,投毒。
还遇到过,抓取代理IP时,某个网站需要调用API来checkuser,只需要调用,哪怕check失败了;如果不调用的话,就得不到数据。这就跟上面差不多。
这时候用必要使用selenium/phantomjs了,但是会大大降低效率。
还想到的其他反爬虫策略也有改动页面结构。
5.2验证码
当网站判断你是个爬虫时,经常会跳出几个验证码来打断你……的腿。处理验证码,常用的方法有:
1.频率不高,数据量不大时,可以手动输入
2.频率高,数据量大,考虑接入打码平台。你们知道吗,这叫服务,得花钱!
3.当以上行不通时,自己写程序识别验证码啊。我会说我本科毕设做的就是基于深度神经网络的验证码识别吗?会!由于做的不成熟,就不拿出来现眼了。验证码识别在知乎和GitHub上都有相关的项目,可以参考做一下。
4.还有哪些非传统的验证码,例如12306的选择图片、滑块验证、记录鼠标轨迹和点击等等。12306那个可以调用谷歌或者百度的识图API,滑块和鼠标轨迹之类的得借助phantomjs。等我学会了我再讲出来。这里会耗费精力很大。多多sleep会在很大程度上避免验证码。
5.3登录
还在学习这个,懂得不多,不吹牛皮,推荐Github项目:模拟登录一些知名的网站,为了方便爬取需要登录的网站
5.4cookie
大多数不是真心想要反爬虫的网站,或者保护重要数据,不会查你的cookie。推荐几篇关于cookie的文章,别的以后想起来再补充。
Find Hao——python爬虫学习(四)获取cookie
州的先生——Python爬虫实战入门四:使用Cookie模拟登录——获取电子书下载链接
5.5总结
其实绕过这些反爬虫手段才是王道,正面刚得费多少精力。间隔时间大一点儿,并发量小一点儿,不给人家服务器造成压力,人家才不会盯上你。出来混都不容易,谁还不是个宝宝。
6.推荐
1.有能力的去阅读Scrapy源码
2.推荐书目:《HTTP权威指南》《用python写网络爬虫》,前端方面我是小白,就知道本《CSS权威指南》求大家推荐基础的前端书籍。
3.python相关的:路人甲大神的python总结,还有知乎上好多python大牛,多多关注他们
4.我这儿还有一些python的电子书,包括网上的和我自己买的,都贡献出来
链接:http://pan.baidu.com/s/1skEQeTZ 密码:gi91
5.正则表达式相关的,我倒是经常看见这个大哥(国服第一奇葩辅助)回答
This article was written by ZKeeer
文章导航
2 THOUGHTS ON “简单爬虫的通用步骤”
- 小咸鱼说道:
博主,你的VPS配置是多少的?多少的配置可以满足日常爬虫需要?
- ZKeeer说道:
我的博客vps是vultr家的,1核512M,挺便宜的,推荐一下https://www.vultr.com/?ref=7170172
如果要单纯用vps部署爬虫,要看爬虫的设计来决定vps配置了。
发
pyrthon 简单爬虫实现的更多相关文章
- Python简单爬虫入门三
我们继续研究BeautifulSoup分类打印输出 Python简单爬虫入门一 Python简单爬虫入门二 前两部主要讲述我们如何用BeautifulSoup怎去抓取网页信息以及获取相应的图片标题等信 ...
- [Java]使用HttpClient实现一个简单爬虫,抓取煎蛋妹子图
第一篇文章,就从一个简单爬虫开始吧. 这只虫子的功能很简单,抓取到”煎蛋网xxoo”网页(http://jandan.net/ooxx/page-1537),解析出其中的妹子图,保存至本地. 先放结果 ...
- 简单爬虫,突破IP访问限制和复杂验证码,小总结
简单爬虫,突破复杂验证码和IP访问限制 文章地址:http://www.cnblogs.com/likeli/p/4730709.html 好吧,看题目就知道我是要写一个爬虫,这个爬虫的目标网站有 ...
- Python简单爬虫入门二
接着上一次爬虫我们继续研究BeautifulSoup Python简单爬虫入门一 上一次我们爬虫我们已经成功的爬下了网页的源代码,那么这一次我们将继续来写怎么抓去具体想要的元素 首先回顾以下我们Bea ...
- GJM : Python简单爬虫入门(二) [转载]
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- python 简单爬虫diy
简单爬虫直接diy, 复杂的用scrapy import urllib2 import re from bs4 import BeautifulSoap req = urllib2.Request(u ...
- python3实现简单爬虫功能
本文参考虫师python2实现简单爬虫功能,并增加自己的感悟. #coding=utf-8 import re import urllib.request def getHtml(url): page ...
- Python开发简单爬虫 - 慕课网
课程链接:Python开发简单爬虫 环境搭建: Eclipse+PyDev配置搭建Python开发环境 Python入门基础教程 用Eclipse编写Python程序 课程目录 第1章 课程介绍 ...
- nodejs的简单爬虫
闲聊 好久没写博客了,前几天小颖在朋友的博客里看到了用nodejs的简单爬虫.所以小颖就自己试着做了个爬博客园数据的demo.嘻嘻...... 小颖最近养了条泰日天,自从养了我家 ...
随机推荐
- 使用OpenCV读、操作、写图像并与bash合作对某个文件夹下全部图像进行相似处理
我门要对某个文件夹下全部图像文件进行统一处理,假设图像的数量过多.那么手动地一张张处理就会显得有些麻烦.本文使用OpenCV和bash来完毕我们指定的任务. 任务 将文件夹A下的全部统一格式的jpg图 ...
- Android帧动画实现,防OOM,比原生动画集节约超过十倍的资源
2015年项目接到一个需求,实现一个向导动画,这个动画一共六十张图片,当时使用的是全志A33的开发(512的内存),通过使用Android的动画集实现,效果特别卡顿,然后想到这样的方式来实现,效果非常 ...
- 剑指offer面试题24-二叉搜索树的后序遍历序列
题目: /* * 输入一个整数数组,推断该数组是不是某二叉搜索树的兴许遍历的结果.<br/> * 假设是则返回true,否则返回false.<br/> * 如果输入的数组 ...
- Codeforces Round #311 (Div. 2) D - Vitaly and Cycle
D. Vitaly and Cycle time limit per test 1 second memory limit per test 256 megabytes input standard ...
- GCD总结(一)
GCD为我们提供了三种类型的调度队列(dispatch queue),分别为串行,并行和主调度队列. 串行(Serial) 你可以创建任意个数的串行队列,每个队列依次执行添加的任务,一个队列同 ...
- mybatis中各种数据的映射类型
Mybatis对应的java和数据库的数据类型,最后有图片 Mybatis java ...
- Objective-C 声明属性
创建: 2018/01/24 完成: 2018/01/25 遗留: TODO 声明属性(declared property) 属性的声明与功能 属性的声明 @property 读写 @proper ...
- ionic安卓打包apk--安卓签名
上周项目上线,在网上看了看打包的博客,感觉不是很清晰我自己来总结下 首先,我们在项目的根目录下 build android apk 的时候执行的命令一定要是 ionic build android - ...
- BEM --Yandex的CSS 命名方法论
人们问我最多的问题之一是在CSS类名中--和__是什么意思?它们的出现是源于BEM和Nicolas Gallagher... BEM的意思就是块(block).元素(element).修饰符(modi ...
- 运行Tomcat 遇到的问题以及解决总结
本文持续更新…… 情况一:在eclipse中启动tomcat提示 1. Server Tomcat v8.5 Server at localhost failed to start . 解决方案 方法 ...