爬虫学习笔记

2016.10.27

在一开始按照例子写最简单的爬虫的时候居然都写错了,写成了如下的

1
2
3
4
5
6
7
import urllib2
req = urllib2.Request('http://example.com')
#response = urllib2.urlopen('http://python.org')
response = urllib2.urlopen(req)
html = response.read
f = open("out.txt","w")
print >> f,html

html = response.read()这里少加了个括号,导致赋值的内容不是爬到的内容,而是read的属性?

1
<bound method _fileobject.read of <socket._fileobject object at 0x7fea52107bd0>>

得到了这种奇怪的东西

URLError 通常,URLError在没有网络连接(没有路由到特定服务器),或者服务器不存在的情况下产生。 这种情况下,异常同样会带有"reason"属性,它是一个tuple(可以理解为不可变的数组), 包含了一个错误号和一个错误信息。 我们建一个urllib2_test06.py来感受一下异常的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
import urllib2

req = urllib2.Request('http://www.example.com')

f = open("out.txt","w")

try: urllib2.urlopen(req)

有些东西用js解密的方法,python实在是不会啊


except urllib2.URLError, e:
print >>f ,e.reason

按下F5,可以看到打印出来的内容是: [Errno 11001] getaddrinfo failed 也就是说,错误号是11001,内容是getaddrinfo failed

i have no answer.

1
2
3
4
5
6
7
8
9
10
11
12
import urllib2

req = urllib2.Request('http://bbs.csdn.net/callmewhy')

f = open("out.txt","w")

try: urllib2.urlopen(req)


except urllib2.URLError, e:
print >>f ,e.reason
#print >> f, e.code

2.HTTPError 服务器上每一个HTTP 应答对象response包含一个数字"状态码"。 有时状态码指出服务器无法完成请求。默认的处理器会为你处理一部分这种应答。 例如:假如response是一个"重定向",需要客户端从别的地址获取文档,urllib2将为你处理。 其他不能处理的,urlopen会产生一个HTTPError。 典型的错误包含"404"(页面无法找到),"403"(请求禁止),和"401"(带验证请求)。 HTTP状态码表示HTTP协议所返回的响应的状态。 比如客户端向服务器发送请求,如果成功地获得请求的资源,则返回的状态码为200,表示响应成功。 如果请求的资源不存在, 则通常返回404错误。 HTTP状态码通常分为5种类型,分别以1~5五个数字开头,由3位整数组成: ------------------------------------------------------------------------------------------------ 200:请求成功 处理方式:获得响应的内容,进行处理 201:请求完成,结果是创建了新资源。新创建资源的URI可在响应的实体中得到 处理方式:爬虫中不会遇到 202:请求被接受,但处理尚未完成 处理方式:阻塞等待 204:服务器端已经实现了请求,但是没有返回新的信 息。如果客户是用户代理,则无须为此更新自身的文档视图。 处理方式:丢弃 300:该状态码不被HTTP/1.0的应用程序直接使用, 只是作为3XX类型回应的默认解释。存在多个可用的被请求资源。 处理方式:若程序中能够处理,则进行进一步处理,如果程序中不能处理,则丢弃 301:请求到的资源都会分配一个永久的URL,这样就可以在将来通过该URL来访问此资源 处理方式:重定向到分配的URL 302:请求到的资源在一个不同的URL处临时保存 处理方式:重定向到临时的URL 304 请求的资源未更新 处理方式:丢弃 400 非法请求 处理方式:丢弃 401 未授权 处理方式:丢弃 403 禁止 处理方式:丢弃 404 没有找到 处理方式:丢弃 5XX 回应代码以“5”开头的状态码表示服务器端发现自己出现错误,不能继续执行请求 处理方式:丢弃 ------------------------------------------------------------------------------------------------ HTTPError实例产生后会有一个整型'code'属性,是服务器发送的相关错误号。 Error Codes错误码 因为默认的处理器处理了重定向(300以外号码),并且100-299范围的号码指示成功,所以你只能看到400-599的错误号码。 BaseHTTPServer.BaseHTTPRequestHandler.response是一个很有用的应答号码字典,显示了HTTP协议使用的所有的应答号。 当一个错误号产生后,服务器返回一个HTTP错误号,和一个错误页面。 你可以使用HTTPError实例作为页面返回的应答对象response。 这表示和错误属性一样,它同样包含了read,geturl,和info方法。 我们建一个urllib2_test07.py来感受一下:

i have no answer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import urllib
import urllib2

url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
values = {'name': 'Michael Foord',
'location': 'Northampton',
'language': 'Python' }
headers = {'User-Agent': user_agent}

data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
the_page = response.read()

f = open("out.txt","w")
print >> f, the_page

this has been unacceptable.

2017.11.18
使用selenium+PhantomJS
编写爬虫时,如果单纯是静态网站,Nodejs的cheerio,requests以及Python的urlib、urlib2与request(BeautifulSoup)就能解决需求。如果碰上网站通过AJAX获取数据或者JS延迟获取数据时。
使用lxml解析器的原因是比较性能之后,还是lxml比较优异,基本都能解析
HTTP请求主要分为Get和Post两类:
GET是从服务器上获取指定页面信息,POST是向服务器提交数据并获取页面信息。
GET请求参数都显示在URL上,服务器根据该请求所包含URL中的参数来产生响应内容。 "Get" 请求的参数 是URL的一部分。
POST请求参数在请求体当中,消息长度没有限制而且以隐式的方式进行发送,通常用来向HTTP服务器提交量比较大的数据(比如请求中包含许多参数或者文件上传操作等)。 "POST"请求的参数 不在URL中,而在请求体中。
页面的form表单一般都有method属性,默认值是"get"。
举个栗子,登录时提交用户名和密码:
如果用"get"方式,提交表单后,则用户输入的用户名和密码将在地址栏中暴露无遗; 如果设置为"post,则提交表单后,地址栏不会有用户名和密码的显示。 所以处理登录页面的form表单时,发送的请求都是"POST"方式。

wsgi协议


字节协议

没找到为什么,python socket建立服务器的时候,浏览器始终接收不到数据,接收到也显示不出来,不明白为什么

chrome和safari不显示,但是firefox显示了,先用firefox,以后再想这几个之间的区别

去掉了HTTP/1.1的表头,居然还能显示出来

换行符协议

特殊字符分割协议

二进制字符长度定义协议

为什么使用301永久重定向呢?

一个解答是为了SEO

启动和停止ssdb服务

启动:ssdb-server /usr/local/etc/ssdb.conf 守护进程启动方式 ssdb-server -d /usr/local/etc/ssdb.conf 停止: ssdb-server /usr/local/etc/ssdb.conf -s stop 重启:停止: ssdb-server /usr/local/etc/ssdb.conf -s restart 启动客户端:ssdb-cli

支持数据类型

SSDB ⽀持三种数据类型, 别分是 KV(key-value), Hashmap(map), Zset(sorted set).

requests爬取中文乱码

使用str(html.content,"utf-8")搞定

http://www.cnblogs.com/bitpeng/p/4748872.html

StringIO这个模块在python3里有点不一样了,存在了Io模块里,没办法直接用别人的资料了

保存图片在本地

1
2
3
4
5
6
7
8
9
10
11
12
13
from PIL import Image
import io
import requests

html = requests.get("http://jwxt.bupt.edu.cn/validateCodeAction.do?random=")
f = open('code.jpg','wb')
f.write(html.content)
f.close()
img = Image.open('code.jpg')


#'/Users/sean10/Desktop/code.jpg'

而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。

这些队列都实现了锁原语,能够在多线程中直接使用

python并发

从threading到multiprocess,再到concurrent.future支持

jsessionid似乎是j2ee的东西


beautifulSoup的对象,如何打印出来,他的子对象tag对象没有text方法

常见的反爬策略主要有:

IP限制

UA限制

Cookie限制

资源随机化存储

动态加载技术

……

对应的反爬处理手段主要有:

IP代理池技术

用户代理池技术

Cookie保存与处理

自动触发技术

抓包分析技术+自动触发技术

有效地存储(数据库应该怎样安排) 有效地判重(这里指网页判重,咱可不想把人民日报和抄袭它的大民日报都爬一遍) 有效地信息抽取(比如怎么样抽取出网页上所有的地址抽取出来,“朝阳区奋进路中华道”),搜索引擎通常不需要存储所有的信息,比如图片我存来干嘛... 及时更新(预测这个网页多久会更新一次)

BeautifulSoup找不到解析的元素的原因,似乎是因为网页编码问题,比如学校教务系统用的是GBK,不是utf-8,就不好搞了

在使用Beautifulsoup过程中,对于大多数html源码,通过指定正确的编码,或者本身是默认UTF-8编码而无需指定编码类型,其都可以正确解析html源码,得到对应的soup变量。

然后就接着去利用soup实现你所想要的功能了。

但是有时候会发现,有些html解析后,有些标签等内容丢失了,即所得到的soup不是所期望的完整的html的内容。

这时候,很可能遇到了非法的html,即其中可能包含了一些不合法的html标签等内容,导致Beautifulsoup虽然可以解析,没有报错,但是实际上得到的soup变量,内容缺失了一部分了。

比如我就遇到过不少这样的例子:

部分Blogbus的帖子的html中非html5和html5的代码混合导致Beautifulsoup解析错误 之前在为BlogsToWordPress添加Blogbus支持过程中去解析Blogbus的帖子的时候,遇到一个特殊的帖子:http://ronghuihou.blogbus.com/logs/89099700.html,其中一堆的非html5的代码中,包含了这样一段html5的代码的写法,即标签属性值不加括号的:

1
2
3
4
<SCRIPT language=JavaScript>
document.oncontextmenu=new Function("event.returnValue=false;"); //禁止右键功能,单击右键将无任何反应
document.onselectstart=new Function( "event.returnValue=false;"); //禁止先择,也就是无法复制
</SCRIPT language=JavaScript>

结果导致Beautifulsoup解析错误,得到的soup中,找不到所需要的各种class等属性值。

对应的解决办法就是,把这部分的代码删除掉,然后再解析就可以了:

其中一堆的非html5的代码中,包含了这样一段html5的代码的写法,即标签属性值不加括号的:

1
2
3
4
5
6
7
8
9
foundInvliadScript = re.search("<SCRIPT language=JavaScript>.+</SCRIPT language=JavaScript>", html, re.I | re.S );
logging.debug("foundInvliadScript=%s", foundInvliadScript);
if(foundInvliadScript):
invalidScriptStr = foundInvliadScript.group(0);
logging.debug("invalidScriptStr=%s", invalidScriptStr);
html = html.replace(invalidScriptStr, "");
logging.debug("filter out invalid script OK");

soup = htmlToSoup(html);

判断浏览器版本的相关代码,导致Beautifulsoup解析不正常 之前在给BlogsToWordpress添加新浪博客的支持的过程中

遇到很多新浪博客的帖子的html中,包含很多判断浏览器版本的相关代码:

<!–[if lte IE 6]> xxx xxx <[endif]–>

由此导致Beautifulsoup解析html不正常。

font标签嵌套层次太多,导致Beautifulsoup无法解析html 接上面那个解析新浪博客帖子的例子,期间又遇到另外一个问题,对于一些特殊帖子:http://blog.sina.com.cn/s/blog_5058502a01017j3j.html

其包含特殊的好几十个font标签且是一个个嵌套的代码,导致无法Beautifulsoup无法解析html,后来把对应嵌套的font标签删除掉,才可以正常解析。

相关python代码为:

handle special case for http://blog.sina.com.cn/s/blog_5058502a01017j3j.html

processedHtml = processedHtml.replace('', ""); processedHtml = processedHtml.replace("", "");

遇到其他类似的问题,也可以去删除或替换出错代码,即可解决问题。

不过需要说明的是,很多时候,你未必很容易就找到出错的代码。

想要找到出错的代码,更多的时候,需要你一点点调试,一点点的删除看似可疑的一些html源码,然后最终才能定位到出错的代码,然后删除掉后,才可以正常工作的。


uri资源文件如何获取,似乎有格式

似乎是jquery自带的延迟加载技术

像淘宝或者京东这样的APP页面上有很多图片,当我们滑到下一屏时下一屏的图片才会加载,这就采用了图片懒加载的方式.

图片懒加载,简单来说就是在页面渲染过程中,图片不会一次性全部加载,会在需要的时候加载,比如当滚动条滚动到某一个位置时触发事件加载图片,如下代码:

通过js将img标签的data-src属性赋值给src属性

但是如果是用base64的,在dom里属性就消失不见了

针对decode base64编码的图片比较慢的问题,我们可以选择使用canvas来加速.当向canvas发出绘画命令时,浏览器直接将指令发到图形加速器而不需要开发者更多的干预,硬件图形加速器则以难以执行的运算速度实时绘画和渲染图形.因此,我们可以使用canvas来渲染base64编码后的图片

Inlined Placeholder Image To reduce the amount of request you can use data uri images as the placeholder.

最后解析出来的base64居然不是图片,也是怪奇怪的

http://api.nicodic.jp/

豆瓣获取url还是非常简单的,就是被403了,明天再看了吧,还有个数据库连接池多线程的问题需要试试。


python的json.dumps方法默认会输出成这种格式"",。

要输出中文需要指定ensure_ascii参数为False.

1
output = json.dump(jsonData,targetFile,ensure_ascii=False,indent=4)

在输出的时候,对文件制定特定的UTF-8编码:

1
2
3
import codecs

with codecs.open(path,'w','utf-8') as w:

框架使用

主要期望了解一下目前社区始终更常用的类似低代码的配置化实现方案.

scrapy

1
2
3
4
5
6
7
8
9
10


scrapy shell "https://quotes.toscrape.com/page/1/"


scrapy genspider itcast "itcast.cn"


cd tutorial
scrapy crawl itcast

剩下主要是item和spider之间组合的问题

使用一个parse函数的输出作为另一个函数的输入

通过对parse函数的callback嵌套触发Request

Using multiple spiders at in the project in Scrapy - Stack Overflow

python - Scrapy - parse a page to extract items - then follow and store item url contents - Stack Overflow

动态目录实现

关键是以下两个ImagesPipeline的函数重载

  • get_media_requests
  • image_paths

Scrapy框架之利用ImagesPipeline下载图片 - 简书

Downloading and processing files and images — Scrapy 2.8.0 documentation

1
2
3
def file_path(self, request, response=None, info=None, *, item=None):
image_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()
return f"full/{image_guid}.jpg"

奇怪, 如果按照ImagesPipeline的函数, 这里应该是full才对, 所以到底file_path是什么时候感知到settings里的IMAGES_STORE的呢? ## PySpider

订阅源 学习

rsshub

legado

书源

订阅源

最终