本文知识点: requests, cookie, BeautifulSoap, redis
今天天气不错,应该出去走走,我决定去客厅散散心。打开某信出的阅读软件,以前添加的几百本列在那里,感觉都挺好看,可怎么选出最值得看的呢?软件没有书单和排行榜功能,但每本书都有分数和评论人数。一个个点开统计太累人了。这种重复性的工作当然要用编程来解决啊。
软件提供了Web端,我们只需要拿出requests这个大杀器就可以用普通的get方法就可以了。然而拿到的html提示没登录。为什么呢?因为服务器端认为requests是一个没有登录过的浏览器,所以提示登录。但真要登录的话又需要扫二维码。这就不方便了。
登录往往是爬虫程序的大敌,一般解决思路是selenium打开浏览器,输入用户名和密码登录后获取cookie. 但往往会遇上验证码,这还要用AI来识别。但我们的情况不需要登录多次,所以只要拿到登录过的cookie就好了。
1580015148113.png
用Chrome的同学只要打开开发者工具,选Network,按上头所示就可以拿到cookie, 拷贝后就可以放在程序里了。可是Cookie会过期的,那我们又需要拷贝一次。任何一个有追求的程序员是不会忍受手工操作的。既然Cookie是个文件,那肯定在硬盘上。那我们用Python读取就好了。
def get_cookie_from_chrome(host):
cookie_path = os.environ['LOCALAPPDATA'] + r"\Google\Chrome\User Data\Default\Cookies"
sql = "select host_key,name,encrypted_value from cookies where host_key='%s'" % host
with sqlite3.connect(cookie_path) as conn:
cu = conn.cursor()
cookies = {name: CryptUnprotectData(encrypted_value)[1].decode() for host_key, name, encrypted_value in
cu.execute(sql).fetchall()}
# for host_key, name, encrypted_value in cu.execute(sql).fetchall():
# print(f"{host_key}:{name}")
return cookies
最新版的Chrome是用sqlite管理Cookie的,我们找到Chrome的存放路径后,跑个SQL就可以了。
有了Cookie,我们就可以去拿任意网页资源了。因为Requests默认的User-Agent可以会被反爬虫程序检测到,所以需要替换为当前浏览器的。同时要注意返回数据可能会出现乱码,需要指定为UTF-8或者GBK等
def get_page(url):
global cookie
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36'}
result = requests.get(url, cookies=cookie, headers=headers)
result.encoding = 'utf-8'
return result.text
拿到整个网页后,就需要解析Html来获取我们感兴趣的资源。懂正则的高手这时就可以离开了。但普通程序员还是需要一个html的解析器,比如BeautifulSoup.
shelfBooks = shelf.find_all(attrs={'class': 'shelfBook'})
books = []
for b in shelfBooks:
try:
books.append(Book(root + b.get('href'), b.select('div.title')[0].text))
except Exception as e:
print(f"{b} got {e}")
用find_all通过css class名就可以拿到书架上所有的书,获取它们的详细链接页,然后再用reqeusts获取详细页拿数据。这样的批量操作自然会引起注意。所以每个请求都要sleep下随机时间。
网络有时不是很稳定,如果没有用多线程的话,一次失败就导致前面的结果丢失了。一方面我们要多用try..except。另一方面我们要随时保存数据。数据库就没什么意思了,来个最近流行的redis吧。 果然非常好用,get和set方法就可以搞定了
r = redis.Redis(host='192.168.1.250', port=6379, decode_responses=True)
r.set(book.title, json.dumps(book, cls=UserEncoder))
...
saved_book = r.get(book.title)
你可以用简单的字符器,也可以用json。
拿到所有数据后,自然可以按你的需要排序,筛选需要的书了。不知不觉,天怎么黑了
网友评论