Download image with selenium python
Asked Answered
J

9

63

I want get captcha image from browser. I have got a url of this picture, but the this picture changes each updated time (url is constant).

Is there any solution to get picture from browser (like 'save picture as' button)?

From the other hand, I think it should be work:

  1. get screenshot of the browser
  2. get position of picture
  3. crop captcha from screenshot using opencv

link of the dynamic capcha - link

The problem was solved via screenshot:

browser.save_screenshot('screenshot.png')
img = browser.find_element_by_xpath('//*[@id="cryptogram"]')
loc = img.location

image = cv.LoadImage('screenshot.png', True)
out = cv.CreateImage((150,60), image.depth, 3)
cv.SetImageROI(image, (loc['x'],loc['y'],150,60))
cv.Resize(image, out)
cv.SaveImage('out.jpg', out)

Thanks

Jody answered 28/6, 2013 at 9:42 Comment(8)
do you want a snapshot of the browser or a certain image from the pagePlot
I need captcha image from the page. Snapshoot is a way to get it.Jody
what is the module 'cv' ?, nevermind--see: opencvKemppe
@erm3nda, apparently you don't know how to read past commas or understand dates.Kemppe
What i don't know is the need of put here "hey i don't know X when you can search it instead". What's your real profit of doing that? Please, watch my next comment and say me what do you think about.Lullaby
whats "find_element_by_xpath"? nevermind-see: find_element_by_xpath.Lullaby
And 'cv' commas content is assumed to be the words that the user used when created the opencv instance. The same thing when you create a new Selenium webdriver object.Lullaby
Check this https://mcmap.net/q/303646/-how-to-download-an-image-using-selenium-any-versionKala
B
71

Here's a complete example (using google's recaptcha as a target):

import urllib
from selenium import webdriver

driver = webdriver.Firefox()
driver.get('http://www.google.com/recaptcha/demo/recaptcha')

# get the image source
img = driver.find_element_by_xpath('//div[@id="recaptcha_image"]/img')
src = img.get_attribute('src')

# download the image
urllib.urlretrieve(src, "captcha.png")

driver.close()

UPDATE:

The problem with dynamic generated images is that there is a new image generated each time you request it. In that case, you have several options:

  • take a screenshot

     from selenium import webdriver
    
     driver = webdriver.Firefox()
     driver.get('https://moscowsg.megafon.ru/ps/scc/php/cryptographp.php?PHPSESSID=mfc540jkbeme81qjvh5t0v0bnjdr7oc6&ref=114&w=150')
    
     driver.save_screenshot("screenshot.png")
    
     driver.close()
    
  • simulate right click + "Save As". See this thread for more info.

Bolzano answered 28/6, 2013 at 10:6 Comment(10)
google recapcha has static url. linkJody
Then, give a link where it's dynamic.Bolzano
link of the capcha - linkJody
Additionally, could you provide a link to the whole page with captcha?Bolzano
link I save problem via screenshot. It is not efficient, but works. See head questionJody
@alecxe,thanks for your post, i simulated your code and it worked for me, can you please tell me how did figure out the xpath for the image ('//div[@id="recaptcha_image"]/img') , thanks alotNord
In the last couple of years.. urllib has changed. Instead of using urllib.urlretrieve, you now need to use urllib.request.urlretrieve.Uraemia
can selenium itself download the image in the same session inside the automated browser?Impassible
to use urllib.request.urlretrieve first import urllib.request otherwise it will throw errorAberdare
this will not work if accessing the image requires authenticationScamp
B
35

It's ok to save a screenshot from the whole page and then cut the image from, but you can also to use the "find" method from "webdriver" to locate the image you want to save, and write the "screenshot_as_png" property like below:

from selenium import webdriver
driver = webdriver.Firefox()
driver.get('https://www.webpagetest.org/')
with open('filename.png', 'wb') as file:
    file.write(driver.find_element_by_xpath('/html/body/div[1]/div[5]/div[2]/table[1]/tbody/tr/td[1]/a/div').screenshot_as_png)

Sometimes it could get an error because of the scroll, but depending on your necessity, it's a good way to get the image.

Beekman answered 22/5, 2019 at 11:19 Comment(3)
@ Ramon this does not work, I am trying to get the image of your profile pic from this page and get the error elenium.common.exceptions.WebDriverException: Message: unknown command: session/5734e4b0f8d6171317af42ddf0979562/element/0.9586500909174849-1/screenshotWoodwork
So, @Jortega. Try sending your code here, then I can check if I find same mistake.Beekman
But what if the image is a gif? Is there anyway get download that gif file?Cordeliacordelie
R
7

The problem of using save_screenshot is that we cannot save an image in its original quality and cannot restore the alpha channel in an image. Therefore, I propose another solution. Here is a complete example using the selenium-wire library suggested by @codam_hsmits. It is possible to download images via ChromeDriver.

I have defined the following function to parse each request and save the request body to a file when necessary.

from seleniumwire import webdriver  # Import from seleniumwire
from urllib.parse import urlparse
import os
from mimetypes import guess_extension
import time
import datetime

def download_assets(requests,
                   asset_dir="temp",
                   default_fname="unnamed",
                   skip_domains=["facebook", "google", "yahoo", "agkn", "2mdn"],
                   exts=[".png", ".jpeg", ".jpg", ".svg", ".gif", ".pdf", ".bmp", ".webp", ".ico"],
                   append_ext=False):
    asset_list = {}
    for req_idx, request in enumerate(requests):
        # request.headers
        # request.response.body is the raw response body in bytes
        if request is None or request.response is None or request.response.headers is None or 'Content-Type' not in request.response.headers:
            continue
            
        ext = guess_extension(request.response.headers['Content-Type'].split(';')[0].strip())
        if ext is None or ext == "" or ext not in exts:
            #Don't know the file extention, or not in the whitelist
            continue
        parsed_url = urlparse(request.url)
        
        skip = False
        for d in skip_domains:
            if d in parsed_url.netloc:
                skip = True
                break
        if skip:
            continue
        
        frelpath = parsed_url.path.strip()
        if frelpath == "":
            timestamp = str(datetime.datetime.now().replace(microsecond=0).isoformat())
            frelpath = f"{default_fname}_{req_idx}_{timestamp}{ext}"
        elif frelpath.endswith("\\") or frelpath.endswith("/"):
            timestamp = str(datetime.datetime.now().replace(microsecond=0).isoformat())
            frelpath = frelpath + f"{default_fname}_{req_idx}_{timestamp}{ext}"
        elif append_ext and not frelpath.endswith(ext):
            frelpath = frelpath + f"_{default_fname}{ext}" #Missing file extension but may not be a problem
        if frelpath.startswith("\\") or frelpath.startswith("/"):
            frelpath = frelpath[1:]
        
        fpath = os.path.join(asset_dir, parsed_url.netloc, frelpath)
        if os.path.isfile(fpath):
            continue
        os.makedirs(os.path.dirname(fpath), exist_ok=True)
        print(f"Downloading {request.url} to {fpath}")
        asset_list[fpath] = request.url
        try:
            with open(fpath, "wb") as file:
                file.write(request.response.body)
        except:
            print(f"Cannot download {request.url} to {fpath}")
    return asset_list

Let's download some images from Google homepage to temp folder.

# Create a new instance of the Chrome/Firefox driver
driver = webdriver.Chrome()

# Go to the Google home page
driver.get('https://www.google.com')

# Download content to temp folder
asset_dir = "temp"

while True:
    # Please browser the internet, it will collect the images for every second
    time.sleep(1)
    download_assets(driver.requests, asset_dir=asset_dir)

driver.close()

Note that it cannot decide which images can be seen on the page rather than being hidden in the background, so the users should actively click the buttons or links to trigger new download requests.

Runstadler answered 11/10, 2020 at 8:22 Comment(0)
C
3

So, just to stay relevant, here is a 2020 solution using seleniumwire, which is a package that gives you access to the requests made in a browser. You can use it easily as follows:

from seleniumwire import webdriver

# Sometimes, selenium randomly crashed when using seleniumwire, these options fixed that.
# Probably has to do with how it proxies everything.
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')

driver = webdriver.Chrome(chrome_options=options)
driver.get("https://google.com")

for request in driver.requests:
    # request.path
    # request.method
    # request.headers
    # request.response is the response instance
    # request.response.body is the raw response body in bytes

    # if you are using it for a ton of requests, make sure to clear them:
    del driver.requests

Now, why would you need this? Well, for example for ReCaptcha bypassing, or for bypassing something like Incapsula. Use this at your own risk.

Conative answered 22/8, 2020 at 11:54 Comment(0)
D
2

You can download images without losing quality with JS:

from io import BytesIO
from PIL import Image
from base64 import b64decode

driver.get(url)

# Create a canvas, set it's width and height equal to image's
# Write image to canvas, translate to base64
# Remove metadata prefix
b64img = driver.execute_script(r'''
var img = document.getElementsByTagName("img")[0];
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
''')

# Decode from base64, translate to bytes and write to PIL image
img = Image.open(BytesIO(b64decode(b64img)))
Doting answered 27/10, 2022 at 21:1 Comment(1)
Is a valid solution but this will fail in most of the cases: It will throw you this error #22711127Placidia
H
1

Use the following code to download the image

from selenium import webdriver
#set chromedriver.exe path
driver = webdriver.Chrome()
driver.implicitly_wait(0.5)
#maximize browser
driver.maximize_window()
#launch URL
driver.get("name of webpage from where you want to download image");
#open file in write and binary mode
with open('Logo.png', 'wb') as file:
#identify image to be captured
   l = driver.find_element_by_xpath('//*[@alt="name in the alt of image"]')
#write file
   file.write(l.screenshot_as_png)
#close browser
driver.quit()
Hallowell answered 11/7, 2022 at 6:41 Comment(1)
Nice trick but is does not really "download" the image, it screenshot it into a png. This can lead to quality loss and/or large size.Locality
E
1

If you need authorization to download an image, I found the best solution is to combine selenium and selenium-requests:

  1. Use selenium to log in to the website
  2. Parse website to get the image url
  3. Use selenium-requests to get the image using code similar to
response = driver.request("GET", image_url, stream=True)
response.raise_for_status()
with open(path, 'wb') as f:
    response.raw.decode_content = True
    shutil.copyfileobj(response.raw, f) 

as per How to download image using requests

Escaut answered 30/1, 2023 at 21:3 Comment(0)
N
0

Here it is.

  • open the image with Selenium WebDriver
  • extract the width and the height of the image with BeautifulSoup
  • set the right current window size with driver.set_window_size, and take a screenshot with driver.save_screenshot
from bs4 import BeautifulSoup
from selenium import webdriver
 
import os
from urllib.parse import urlparse
 
url = 'https://image.rakuten.co.jp/azu-kobe/cabinet/hair1/hb-30-pp1.jpg'
 
filename = os.path.basename(urlparse(url).path)
filename_png = os.path.splitext(filename)[0] + '.png'  # change file extension to .png
 
opts = webdriver.ChromeOptions()
opts.headless = True
driver = webdriver.Chrome(options=opts)
 
driver.get(url)
 
# Get the width and height of the image
soup = BeautifulSoup(driver.page_source, 'lxml')
width = soup.find('img')['width']
height = soup.find('img')['height']
 
driver.set_window_size(width, height) # driver.set_window_size(int(width), int(height))
driver.save_screenshot(filename_png)

It also works for Google's image format, WebP.

Refer to Downlolad Google’s WebP Images via Take Screenshots with Selenium WebDriver.

Neocene answered 20/2, 2021 at 14:42 Comment(0)
E
0

refer to 'disable_encoding': True

from seleniumwire import webdriver

seleniumwire_options = {
    "disable_encoding": True,
}

driver = webdriver.Chrome(options=options, seleniumwire_options=seleniumwire_options)
driver.get(url) # direct url to image

request = driver.last_request

file = url.split("/")[-1]
with open(file, "wb") as f:
    f.write(request.response.body)
Emlyn answered 13/4, 2023 at 16:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.