python提供了很方便的库来帮助我们实现很多复杂的功能。在编写网络爬虫的过程中,我们可以使用requests来与网站交互并获取网页的源代码,再使用beautifulsoup4对得到的网站源代码(通常是html)进行处理来获取所需要的内容。下面进行详细的介绍。
url(Uniform Resource Locator)也就是我们平时所说的域名,也就是网址。当用户在浏览器中输入网址后,浏览器会先访问本地的hosts文件来寻找是否存在匹配的IP地址,如果没有找到则再通过DNS服务器去获得正确的IP地址进行访问。例如域名www.baidu.com的IP地址为14.215.177.39。
对于一个网页,在浏览器上右键->查看源代码即可查看该网页的实现。网页的源代码大多都是以
text
的html格式,其中成对的p称为标签, 标签后面的键值对称为属性, 标签对之间的部分称为正文。不同的标签显示出来的功能和效果截然不同。我们需要做的就是在这些标签里获取我们需要的信息。给服务器传送数据有多种方式。
1.最简单的就是get方式,在域名后面加上?attr : a=a1&b=b1...x=x1,相当于一个{a:a1,b:b1,c:c1...x:x1}的参数表。
2.稍复杂一些的是post方式传值,像密码这样的需要藏起来的数据自然不能采用明文的get,还有像填一些表格,数据量比较大的也往往采用post方式传值。
requests库可用于访问网页,上传数据等操作。
http://docs.python-requests.org/en/master/
这是requests库的文档,写的很详细。这里我们就简要介绍一些常见的简单用法。
这样我们就有了一个名为r的 Response
对象,通过r.text就可以查看url网页的源代码。
最简单爬虫直接对r.text进行处理就可以获得我们想要的内容。处理的方式有很多种,最简洁的处理方式是采用正则表达式处理,(关于正则表达式的更多内容可以看我之前的博客)比如说我们需要爬取图片,而源代码中图片的url以
"ObjURL":"http:\/\/img3.imgtn.bdimg.com\/it\/u=560465708,1507159867&fm=214&gp=0.jpg"
形式出现,那我们可以采用for x in re.findall(pt , r.text)的方式遍历所需要的图片的url。
其中pt是可与上面html匹配的正则表达式,例如pt = '\"objURL\":\"((http://|https://)[^\"]*)\"'。
在requests中我们就可以通过session来实现这一功能,封装好的session模块很好用也很方便。
比如类似以下代码,就可以向服务器发送post请求,其中data就是我们需要传送的数据。
result = session_requests.post( # 向服务器发送post请求
url,
data=mylog,
headers=dict(referer=url),
)
假设某个网站需要登陆操作,那么怎么爬取登陆过后网页的信息呢?我们就可以使用session模块的post方法,现在问题来了,我们在登陆页面输入了用户名和密码,点击登陆后到底传输了哪些数据呢?只有知道了数据的格式,我们才能进行模拟传输。
我们可以通过下载一个叫Fiddle4的免费软件来实现,Fiddle4会截取浏览器post的内容,因此我们通过这种方式查看login所使用的url和data。
以openjudge为例,当我们login后,在Fiddle4中可以发现data就是
mylog = {"redirectUrl": "http://openjudge.cn/",
"password": "",
"email": ""}
的格式。
好好看看findall就够用了,find的使用和findall几乎相同。参数有多种写法,甚至可以带判别函数和正则表达式的形式,强烈建议看文档!
for tag in find_all( name , attrs , recursive , text , **kwargs )
返回值是beautifulsoup的类的列表。
它可以通过tag["attr"]来访问和修改属性(类似python里的dict),可以通过tag.text访问正文。如果没有符合要求的tag则返回空列表。
find的参数表与findall相同,只是返回值是一个单独的beautifulsoup类或者None
有时间再更...
import requests
import bs4
import re
code_class = {"sh_cpp": ".cpp",
"sh_c": ".c", "": ".py",
"sh_pascal": ".pas",
"sh_java": ".java"}
mylog = {"redirectUrl": "http://openjudge.cn/",
"password": "",
"email": ""}
session_requests = requests.session()
own_url = "http://openjudge.cn/"
#爬取个人主页url下accept的内容,若有next page则继续爬取
def download_url(url):
global session_requests
global own_url
global code_class
ans = session_requests.get(url)
s = str(ans.content, encoding="utf-8")
soup = bs4.BeautifulSoup(s, "html.parser")
blocks = soup.find_all("a", class_="result-right")
#blocks包含accept代码网页的信息,遍历该页
if blocks != []:
for i in blocks:
solution_url = i["href"]
solution = session_requests.get(solution_url)
ss = str(solution.content, encoding="utf-8")
s_soup = bs4.BeautifulSoup(ss, "html.parser")
#判断该题的代码类型
for class_name in code_class:
block = s_soup.find("pre", class_=class_name)
if block == None:
continue
try:
name = s_soup.find_all("h3")[1]
name = name.text[:-5]
except:
print("Get name wrong!")
#去掉第一个':'前面的编号
index = name.find(':')
if (index != -1):
name = name[index + 1:]
#去除题名中的非法字符和开头结尾的空格
name = re.sub(r"[\\/:*?#\"<>|:]", " ", name).strip()
try:
#已存在同名代码
f = open("C:/tmp/" + name + code_class[class_name])
print(name + " has already downloaded")
continue
except IOError:
#不存在同名代码
print("downloading your correct code " + name)
try:
f = open("C:/tmp/" + name + code_class[class_name], 'w', encoding = "utf-8")
new_str = block.text
f.write(new_str)
f.close()
except Exception as e:
print(name + " can't be downloaded correctly")
print(e)
# next 是下一页的相对路径
next = soup.find("a", class_="nextprev", rel="next")
if next != None:
download_url(own_url + next["href"])
def spider():
global code_class
global mylog, session_requests, own_url
mylog["email"] = input("请输入您登陆openjudge使用的email账号:\n")
mylog["password"] = input("请输入您的密码:\n")
login_url = "http://openjudge.cn/api/auth/login/"
result = session_requests.post( # 向服务器发送post请求
login_url,
data=mylog,
headers=dict(referer=login_url),
)
result = session_requests.get("http://openjudge.cn/")
#用正则表达式匹配寻找个人首页的url
pt = r"个人首页"
try:
own_url = re.search(pt, result.text).group(1)
print("这是您的主页:" + own_url)
except:
print("账号不存在或密码错误!请重新输入!")
spider()
return
own_url = ''
download_url(own_url)
print("您已成功下载所有accept的程序至c:\\tmp文件夹下!")
spider()