Python爬虫入门(一)
1.适配环境
note:部分神舟笔记本bios默认开启virtualization technology,可以用任务管理器确认
virtualbox
ubuntu18
vim
python3.6
1.1.ubuntu入门
sudo 给权限
ls 返回当前目录下的所有文件的名称,返回详细信息用ll
apt-get install/remove xxx 下载或删除xxx
mkdir 创建文件夹
pwd 返回当前目录
mv move文件到另一个文件中 mv spder.py test/
find find . -name spider.py
查找名为 spider.py的文件,find ./test/ -name spider.py
就是只对test目录下查找
rm | |
---|---|
rm -f | 强制删除文件 |
rm -rf | 删除文件夹 |
对爬虫有帮助
ps
查看进程
ps a
看到所有进程
ps aux | grep vscode
查看vscode的进程,可以用返回的编号,使用kill 编号
,删除。
grep -r re ./test
在test文档中搜索文件中有re字样的,返回./test/spider.py import re
git clone xxxx.git
复制代码到本地
git pull
对代码进行更新
1.2 vim入门
在非编辑界面输入:
,进入命令模式,:wq
表示写入并退出,:q!
强制退出,:set number
显示行数。
:数字
光标跳到指定数字行,:gg
跳到第一行, :GG
跳到最后一行
h左,j下,k上,l右
在非编辑界面下,按a进入编辑模式,按i进入insert的编辑模式,o表示进入并自动加一个换行。
非编辑模式下按dd,删除光标所在行,按u撤回指令,按x删除一个字节。
文本内复制 1,10 co 16
把1到10行复制到16。用m代替co就是剪切过去
按住shift+insert
将剪切板的东西复制到vim中
2.HTML
<html>是开头,</html>是结束,其中的其他开闭和标签为其的子集
试验一下,可用火狐打开
<a href=“http://www.baidu.com”>百度</a> | 超链接 |
---|---|
<title>xx</title> | 网页标题 |
<h1>xx</h1> | 标题1 |
<p> xx</p> | 段落内容 |
<img src=“www.xxx.com/x.jpg”/> | 图片 |
<!DOCTYPE html>
表示文档类型声明,它的目的是要告诉标准通用标记语言解析器,它应该使用什么样的文档类型定义(DTD)来解析文档。
2.1链接标签
页内跳转和javascrpt的响应
2.2 table标签
方便记忆
tr-table row
th-table head
td-table data
其中boder表示表格边框,caption是大标题
<table>
<captioon> 表格标题</captioon>
<tr> #按行输入,第一行为table head
<th> head1 </th>
<th> head2 </th>
</tr> # 由table head组成的第一行键入完毕
<tr> #按行输入,键入的为td,即单元格内容
<td> data1</td>
<td> data1</td>
</tr> # 由td组成的第二行键入完毕
......
</table>
2.3 DOM属性
给标题h1中加入一个a标签,带name属性,值为turnmoil,则可用超链跳至此处
常见的属性由这几种。
由一个class可以找到一组elements,而id只对应一个element
查询是否有同样的标签出现在网页,右键检查,点进console,键入document.get(用tab补全)
这是按照class的名字来检索的,当然也可以使用tag的名字,用的是document.getElementsByTagName
更进一步的查找
2.4 CSS
style属性
加上style属性,直接更改外观样式。
若要全部都修改,可以用外嵌的方法,先给需要修改的地方加一个class名,再以此建一个css文件,放在在开头,则所有相关的地方都被修改了。此处.main-content就是选择所有class="main-content"的元素的意思。关于更多css选择器,可以看CSS选择器。
3.第一个爬虫
3.1 requests包初探
试试爬一下本篇文章~
成功了,在一堆字符中找到本篇文章
requests.get返回一个类,里面一个属性就是status_code,返回为200,表示爬取成功。
若返回的是json格式的,用resp.json()即可(resp是requests.get赋值的变量),就转为了字典格式。
成功调用 .json() 并不完全意味着响应的成功。有的服务器会在失败的响应中包含一个 JSON 对象(比如 HTTP 500 的错误细节)。这种 JSON 会被解码返回。要检查请求是否成功,请检查.status_code的值是否和你的期望相同。
resp.encoding是编码,本文返回的是UTF-8
看来是正确的。
让爬虫更像人类,需要设置headers,
若直接复制会出现报错,UnicodeEncodeError: 'latin-1' codec can't encode character '\u2026' in position 30
,则是因为直接对header进行复制,应该要点开再复制,因为user-agent太长,中间可能会省略了
注意是要的request header,而不是response header,而且诸如Postman-token, If-Modified-Since 的内容要去掉,本次把cookie也拿掉了。此外,用resp.headers可以返回请求头
3.2 字符编码和文件读写
但是如果encoding发生错误,会导致乱码。这里的新浪文本应该是utf-8,但是requests包把它当成了iso8859-1的编码(多为西欧语系在用,字符少,而中文字符多,不适合),这里open中的"w+",可参见link:
若后面不加b,则说明以字符串方式(unicode)打开
比如这里,需要用ab来打开。比如网上下载图片保存,就要用b,因为是二进制的不是字符串。
编码不同,读出来的内容也就不同。
一些编码相关的知识:
UTF-8可以1到7个字节
py2.7转unicode可以直接u"你好"
str—>(encode)—>bytes,bytes—>(decode)—>str,decode的作用是将其他编码的字符串转换成unicode编码,encode的作用是将unicode编码转换成其他编码的字符串。
在linux系统下,执行如下代码 len(“你好python”) 输出结果正确的是:
Python2.7下中文字符串默认是utf-8编码。
utf-8编码的一个中文占3个字节,2个中文是6个字节,加6个英文字母,输出结果是12
Python3.6下字符串默认是Unicode字符编码, 2个汉字字符加6个英文字符,输出结果是 8
我们加入encoding,得到的网页就是utf-8了。但是返回r.content就不行,因为是encoding不改变content只改变text,所以是字节流(python提示为bytes,而python中的str格式为unicode),要用wb读写。
3.3 爬取文件的命名
直接命名法
如果爬取整个网站,可能会出现同名的情况,所以命名是很重要的
比如新浪的网页,有一个时间2018-12-31的文档,所以doc-ihqfskcn2857665是不是唯一的,我们不知道。
http://sports.sina.com.cn/basketball/nba/2018-12-31/doc-ihqfskcn2857665.shtml
而马蜂窝网站的游记,是直接用一串数字代表,应该就是唯一的
https://www.mafengwo.cn/i/11505790.html
其中,http为协议(也可能为https,ftp和file),www.mafengwo.cn为域名,也叫domain;而basketball/nba/2018-12-31为路径,path;最后的doc-ihqfskcn2857665为网页的名字。我们可以用字符串的内置函数rfind来找(也就是从右边开始搜索,找到右边第一个/的下一个字符到结尾的index,直接冒号后面不加东西也是可以的)
得到:
获得domain:
start_pos = url.find("//")+2
end_pos = url.find("/", start_pos)
domain = url[start_pos: start_end]
filename = domain + "_" + filename # 这里的filename也就是之前的url[url.rfind("/")+1:]
可能会在尾部一个?
,其后面跟着一个参数,可能是浏览器信息或跳转信息,如果留着可能造成爬取网页重复(ref, usr等参数,比如https://baike.baidu.com/item/百科/29?fr=aladdin
,去掉?fr=aladdin
对内容没影响),也可能是用来指明网页的参数(如https://www.youtube.com/watch?v=w3eOMmTjCOQ&index=8&list=PLUM8x224JrX-4MT-9Fp5omT_r5nazCurz
),所以不一定都能删除,可以自己删除看看会不会对网页内容有影响。但大多数情况下对内容没影响,简单的处理方法就是直接删除url[url.find("?")]
。
MD5命名
也就是用连接的hash值直接对文件命名。每个文字的变化都会让md5发生明显变化。另外md5可能会重名,只是概率特别小。在ubuntu中,可以用md5sum 文件名,来查询文件的md5值
我们可以使用hashlib包来搞定,方法如下:
打印:
如果我们使用md5的方法,我们在url的信息就丢失了,比如一些分类信息和url的路径有关,这时我们用数据库储存这些信息。
我们也能用时间戳来命名,但是要用小的时间,比如毫秒。
百度的消歧义方法:同名的底下再加一个参数在后面
3.4 with语法
可以用函数with来省略close的步骤。
with open(filename, "w") as f:
f.write(resp.text)
实质就是
上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
with a() as b, 用_enter_的返回值给a赋值,当with下的语句结束后,调用_exit_方法
给个例子
先执行__init__做初始化,
再根据with语法:
先执行__enter__
再执行do_something
最后无论有无异常出现都必须执行__exit__