scrapy爬取整个网页时如何避免链接失效
最近在使用scrapy爬取网页时遇到很多图片不能正常显示、a标签链接失效的情况,多是因为爬下来的网页和图片路径已经失去了原有的结构,网页无法根据标签的src或者标签的href找到对应的资源,下面就这个问题展开一个小研究。
首先,做这个工作是一定要修改网页的,所以我们引入BeautifulSoup库对网页进行解析。
其次,在本文中所有的网页以域名为目录名进行保存。
soup = bs(response.body, 'html.parser')
for item in soup.find_all('a'):
pass
下面我们分情况讨论。
大体上看,网页中的链接有以下几种格式:
- /page/1.html
- http://website.com/
- http://website.com/1.html
- #content
- ./page/1.html
- page/1.html
- page/1
第一种情况,链接直接以/开头,很明显是从网站的根目录开始检索,那么我们直接将该网站的存储路径加在page前即可(home_path代表当前网页在我们系统中保存的路径,包含域名):
if item.get('href').startswith('/'):
item['href'] = home_path + '/' + item.get('href')[1:]
第二种情况,链接直接以http开头,结尾可能有'/'也可能没有,此时我们直接将其索引至域名目录下的index.html或index.jsp,具体看你要爬的网页的特征,此处只做示例,dir_website为我们保存爬取的网页的路径,不包含域名。
第三种情况,链接仍以http开头,结尾是资源的具体路径,这时候我们不仅要提取域名,还要提取出资源的位置。
elif item.get('href').startswith('http'):
if item.get('href').endswith('.com') or item.get('href').endswith('.com/'):
item['href'] = os.path.join(dir_website, item['href'][item['href'].find('//')+2:item['href'].find('.com')+4], 'index.html')
else:
item['href'] = os.path.join(dir_website, item['href'][item['href'].find('//')+2:])
注意,这个域名一定要从链接中提取,而不是直接插入当前爬取的网站域名,这是因为href里的链接不一定来源于当前网站,有可能索引到其他的网站,这时候如果我们同时爬取了它索引到的网站,通过这种方法我们仍然能够正常访问。
第四种情况——'#content',href里的链接以'#'开头,代表你点击这个链接后在当前页面内查找id='content'的元素,并切换到该元素所在的位置。这种链接我们并不需要处理,直接continue即可。
elif item.get('href').startswith('#'):
continue
第五种和第六种情况我们可以一起处理,这得益于python中path模块的一个方法:
os.path.normpath()
这个方法是什么意思呢,它可以把传入的url的格式调整为正确的格式,比如:
print(os.path.normpath('/home/user//website/index.html'))
#/home/user/website/index.html
print(os.path.normpath('/home/user/./website/index.html'))
#/home/user/website/index.html
print(os.path.normpath('/home/user/../website/index.html'))
#/home/website/index.html
有了这个方法,我们可以直接转换第五种和第六种情况的链接。
else:
item['href'] = os.path.normpath(os.path.join(home_path, item['href']))
至于第七种情况,很明显这种链接是通过向后端的Servlet发送请求来获取页面,而我们爬下来的网页是没有后端的,但是我们确实能够通过scrapy爬到这个网页,那我们就把爬下来的网页加上.html的后缀,然后给这个链接也加上后缀,我们就可以通过点击链接来访问目标资源了。
当然,在那之前,我们得去掉网页链接里的'/',因为我们保存的网页文件名字里是不可能包含'/'的。
if item.get('href').endswith('/'):
item['href'] = item['href'][:-1]
if not item.get('href').endswith('.html'):
item['href'] = item.get('href') + '.html'
至此,我们已经解决了大多数爬下来的网页资源不可达的问题了,以上是对于<a>标签做的处理,<img>标签同理,就不再赘述。
还有一种比较少见的情况,就是网页内的资源url是经过编码的,比如'%'会变成'%25',此时我们只要将保存文件的名称进行解码后再保存即可,相关函数是urllib.unquote()。而网页内的链接无须解码,因为它们在访问资源时会自动解码。
网友评论