I'm using Django and Python 3.7 . I want to speed up my HTML parsing. Currently, I'm looking for three types of elements in my document, like so
req = urllib2.Request(fullurl, headers=settings.HDR)
html = urllib2.urlopen(req).read()
comments_soup = BeautifulSoup(html, features="html.parser")
score_elts = comments_soup.findAll("div", {"class": "score"})
comments_elts = comments_soup.findAll("a", attrs={'class': 'comments'})
bad_elts = comments_soup.findAll("span", text=re.compile("low score"))
I have read that SoupStrainer is one way to improve performacne -- https://www.crummy.com/software/BeautifulSoup/bs4/doc/#parsing-only-part-of-a-document . However, all the examples only talk about parsing an HTML doc with a single strainer. In my case, I have three. How can I pass three strainers into my parsing, or would that actually create worse performance that just doing it the way I'm doing it now?
I don't think you can pass multiple Strainers into the BeautifulSoup constructor. What you can instead do is to wrap all your conditions into one Strainer and pass it to the BeautifulSoup Constructor.
For simple cases such as just the tag names, you can pass a list into the SoupStrainer
html="""
<a>yes</a>
<p>yes</p>
<span>no</span>
"""
from bs4 import BeautifulSoup
from bs4 import SoupStrainer
custom_strainer = SoupStrainer(["a","p"])
soup=BeautifulSoup(html, "lxml", parse_only=custom_strainer)
print(soup)
Output
<a>yes</a><p>yes</p>
For specifying some more logic, you can also pass in a custom function(you may have to do this).
html="""
<html class="test">
<a class="wanted">yes</a>
<a class="not-wanted">no</a>
<p>yes</p>
<span>no</span>
</html>
"""
from bs4 import BeautifulSoup
from bs4 import SoupStrainer
def my_function(elem,attrs):
if elem=='a' and attrs['class']=="wanted":
return True
elif elem=='p':
return True
custom_strainer= SoupStrainer(my_function)
soup=BeautifulSoup(html, "lxml", parse_only=custom_strainer)
print(soup)
Output
<a class="wanted">yes</a><p>yes</p>
As specified in the documentation
Parsing only part of a document won’t save you much time parsing the
document, but it can save a lot of memory, and it’ll make searching
the document much faster.
I think you should check out the Improving performance section of the documentation.
Related
It's my first time using Python and BeautifulSoup. The thing is I'm doing a migration of all articles within a blog from one website to another, and to perform this, I'm extracting certain information from a xml file; the last part of my code tells to extract only the text between the position 0 and 164 from the meta tag, so this way it can appear on google SERP as they want to appear.
The problem here is some articles from the blog has img tags on the first lines inside the tag and I want to remove them, including the src attributes so the code can grab just the text after those img tags.
I tried to solve it in many ways but I did not succeed.
Here is my code:
from bs4 import BeautifulSoup
from urllib2 import urlopen
import csv
import sys
import re
reload(sys)
sys.setdefaultencoding('utf8')
base_url = ("http://pimacleanpro.com/blog?rss=true")
soup = BeautifulSoup(urlopen(base_url).read(),"xml")
titles = soup("title")
slugs = soup("link")
bodies = soup("description")
with open("blog-data.csv", "w") as f:
fieldnames = ("title", "content", "slug", "seo_title", "seo_description","site_id", "page_path", "category")
output = csv.writer(f, delimiter=",")
output.writerow(fieldnames)
for i in xrange(len(titles)):
output.writerow([titles[i].encode_contents(),bodies[i].encode_contents(formatter=None),slugs[i].get_text(),titles[i].encode_contents(),bodies[i].encode_contents(formatter=None)[4:164]])
print "Done writing file"
any help will be appreciated.
Here's a Python 2.7 example that I think does what you want:
from bs4 import BeautifulSoup
from urllib2 import urlopen
from xml.sax.saxutils import unescape
base_url = ("http://pimacleanpro.com/blog?rss=true")
# Unescape to allow BS to parse the <img> tags
soup = BeautifulSoup(unescape(urlopen(base_url).read()))
titles = soup("title")
slugs = soup("link")
bodies = soup("description")
print bodies[2].encode_contents(formatter=None)[4:164]
# Remove all 'img' tags in all the 'description' tags in bodies
for body in bodies:
for img in body("img"):
img.decompose()
print bodies[2].encode_contents(formatter=None)[4:164]
# Proceed to writing to CSV, etc.
The first print statement outputs the following:
<img src='"http://ekblog.s3.amazonaws.com/contentp/wp-content/uploads/2018/09/03082910/decoration-design-detail-691710-300x221.jpg"'><br>
<em>Whether you are up
While the second one after removing the <img> tags is as follows:
<em>Whether you are upgrading just one room or giving your home a complete renovation, it’s likely that your first thought is to choose carpet for all of
Of course you could just remove all image tags in the soup object before creating titles, slugs, or bodies if they're not of interest to you:
for tag in soup("img"):
tag.decompose()
from bs4 import BeautifulSoup
import re
import HTMLParser
import urllib
url = raw_input('enter - ')
html = urllib.urlopen(url).read()
soup = BeautifulSoup(html)
scripts=soup.find_all('script')
for tag in scripts:
try:
Script = tag["src"]
print Script
except:
print "No source"
using this code I m not getting all the java script from html document.
I have checked your code and it seems that you are getting all the javascript. At least you check for all the tags. Of course some of the javascript may be directly embedded into the html and thereby won't have a src attribute. Merely the actual javascript between the <script>...</script> tags. You can get the javscript between these embedded tags using tag.contents in your loop.
Furthermore, I would advise to specify a parser. By default bs4 uses html.parser. Other parsers may perform better/differently. Check out: http://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser
from bs4 import BeautifulSoup
import urllib2
r = urllib2.urlopen('<your url>').read()
soup = BeautifulSoup(r, 'html.parser')
for s in soup.findAll('script'):
print s.get('src')
Im using soup.findAll('table') to try to find the table in an html file, but it will not appear.
The table indeed exists in the file, and with regex Im able to locate it this way:
import sys
import urllib2
from bs4 import BeautifulSoup
import re
webpage = open(r'd:\samplefile.html', 'r').read()
soup = BeautifulSoup(webpage)
print re.findall("TABLE",webpage) #works, prints ['TABLE','TABLE']
print soup.findAll("TABLE") # prints an empty list []
I know I am correctly generating the soup since when I do:
print [tag.name for tag in soup.findAll(align=None)]
It will correctly print tags that it finds. I already tried also with different ways to write "TABLE" like "table", "Table", etc.
Also, if I open the file and edit it with a text editor, it has "TABLE" on it.
Why beautifulsoup doesnt find the table??
Context
python 2.x
BeautifulSoup HTML parser
Problem
bsoup findall does not return all the expected tags, or it returns none at all, even though the user knows that the tag exists in the markup
Solution
Try specifying the exact parser when initializing the BeautifulSoup constructor
## BEFORE
soup = BeautifulSoup(webpage)
## AFTER
soup = BeautifulSoup(webpage, "html5lib")
Rationale
The target markup may include mal-formed HTML, and there are varying degrees of success with different parsers.
See also
related post by Martijn that addresses the same issue
I need the URL inside the description tag of RSS file. I am trying to parse the images in the following link.
"ibnlive.in.com/ibnrss/rss/shows/worldview.xml"
I need the image link in that. I am using urllib and beautiful soup to parse details.
I am trying to parse the title,description,link and images inside the item tag. I can parse the title, description and link. But I can't parse image inside the description tag.
XML:
<item>
<title>World View: US shutdown ends, is the relief only temporary?</title>
<link>http://ibnlive.in.com/videos/429157/world-view-us-shutdown-ends-is-the-relief-only-temporary.html</link>
<description><img src='http://static.ibnlive.in.com/ibnlive/pix/sitepix/10_2013/worldview_1810a_90x62.jpg' width='90' height='62'>The US Senate overwhelmingly approved a deal on Wednesday to end a political crisis that partially shut down the federal government and brought the world's biggest economy to the edge of a debt default that could have threatened financial calamity.</description>
<pubDate>Fri, 18 Oct 2013 09:34:32 +0530</pubDate>
<guid>http://ibnlive.in.com/videos/429157/world-view-us-shutdown-ends-is-the-relief-only-temporary.html</guid>
<copyright>IBNLive</copyright>
<language>en-us</language>
</item>
views.py
from django.conf import settings
from django.shortcuts import render
from django.http import HttpResponse
from django.utils.html import strip_tags
from os.path import basename, splitext
import os
import urllib
from bs4 import BeautifulSoup
def international(request):
arr=[]
#asianage,oneinindia-papers
a=["http://news.oneindia.in/rss/news-international-fb.xml","http://www.asianage.com/rss/37"]
for i in a:
source_txt=urllib.urlopen(i)
b=BeautifulSoup(source_txt.read())
for q in b.findAll('item'):
d={}
d['desc']=strip_tags(q.description.string).strip(' ')
if q.guid:
d['link']=q.guid.string
else:
d['link']=strip_tags(q.comments)
d['title']=q.title.string
for r in q.findAll('description'):
d['image']=r['src']
arr.append(d)
return render(request,'feedpars.html',{'arr':arr})
HTML
<html>
<head></head>
<body>
{% for i in arr %}
<p>{{i.title}}</p>
<p>{{i.desc}}</p>
<p>{{i.guid}}</p>
<img src="{{i.image}}" style="width:100px;height:100px;"><hr>
{% endfor %}
</body>
</html>
Nothing gets displayed in my output.
1/ as I already told you here How to get the url of image in descripttion tag of xml file while parsing? : this is a rss feed so use the appropriate tool: https://pypi.python.org/pypi/feedparser
2/ there's no proper "img" tag in the description, the html markup has been entity-encoded. To get the url, you have to either decode the description's content (to get the tag back) and pass the resulting html fragment to your HTML parser or - since it will probably not be as complex as a full html doc - just use a plain regexp on the encoded content.
At exam.com is not about the weather:
Tokyo: 25°C
I want to use Django 1.1 and lxml to get information at the website. I want to get information that is of "25" only.
HTML exam.com structure as follows:
<p id="resultWeather">
<b>Weather</b>
Tokyo:
<b>25</b>°C
</p>
I'm a student. I'm doing a small project with my friends. Please explain to me easily understand. Thank you very much!
BeautifulSoup is more suitable for html parsing than lxml.
something like this can be helpful:
def get_weather():
import urllib
from BeautifulSoup import BeautifulSoup
data = urllib.urlopen('http://exam.com/').read()
soup = BeautifulSoup(data)
return soup.find('p', {'id': 'resultWeather'}).findAll('b')[-1].string
get page contents with urllib, parse it with BeautifulSoup, find P with id=resultWeather, find last B in our P and get it's content